X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=apps%2Fsilcd%2Fcommand.c;h=b4d5bcee37adf53669b7e5e77dde3ac515eb6dae;hp=80e4ed3637990b488b26cca096e25e2190f2c852;hb=d47a87b03b846e2333ef57b2c0d81f1644992964;hpb=cc9d2eb79d4e4b7b46204b161bce25ccc685f600 diff --git a/apps/silcd/command.c b/apps/silcd/command.c index 80e4ed36..b4d5bcee 100644 --- a/apps/silcd/command.c +++ b/apps/silcd/command.c @@ -35,7 +35,7 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd, SilcCommand command, SilcCommandStatus status, uint32 arg_type, - unsigned char *arg, + const unsigned char *arg, uint32 arg_len); static bool silc_server_command_pending_error_check(SilcServerCommandContext cmd, @@ -131,7 +131,6 @@ static int silc_server_is_registered(SilcServer server, silc_server_command_send_status_reply(cmd, command, SILC_STATUS_ERR_NOT_REGISTERED); - silc_server_command_free(cmd); return FALSE; } @@ -149,6 +148,11 @@ SILC_TASK_CALLBACK(silc_server_command_process_timeout) SilcServerCommandTimeout timeout = (SilcServerCommandTimeout)context; SilcClientEntry client = (SilcClientEntry)timeout->ctx->sock->user_data; + if (!client) { + silc_server_command_free(timeout->ctx); + silc_free(timeout); + } + /* Update access time */ client->last_command = time(NULL); @@ -159,6 +163,8 @@ SILC_TASK_CALLBACK(silc_server_command_process_timeout) timeout->ctx, timeout->cmd->cmd)) timeout->cmd->cb(timeout->ctx, NULL); + else + silc_server_command_free(timeout->ctx); silc_free(timeout); } @@ -181,7 +187,8 @@ void silc_server_command_process(SilcServer server, ctx->packet = silc_packet_context_dup(packet); /* Save original packet */ /* Parse the command payload in the packet */ - ctx->payload = silc_command_payload_parse(packet->buffer); + ctx->payload = silc_command_payload_parse(packet->buffer->data, + packet->buffer->len); if (!ctx->payload) { SILC_LOG_ERROR(("Bad command payload, packet dropped")); silc_buffer_free(packet->buffer); @@ -232,15 +239,12 @@ void silc_server_command_process(SilcServer server, silc_server_command_process_timeout, (void *)timeout, 2 - (time(NULL) - client->last_command), 0, - SILC_TASK_TIMEOUT, - SILC_TASK_PRI_NORMAL); + SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); else silc_schedule_task_add(server->schedule, sock->sock, silc_server_command_process_timeout, - (void *)timeout, - 0, 1, - SILC_TASK_TIMEOUT, - SILC_TASK_PRI_NORMAL); + (void *)timeout, 0, 1, + SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); return; } @@ -250,6 +254,8 @@ void silc_server_command_process(SilcServer server, cmd->cb(ctx, NULL); else if (silc_server_is_registered(server, sock, ctx, cmd->cmd)) cmd->cb(ctx, NULL); + else + silc_server_command_free(ctx); } /* Allocate Command Context */ @@ -296,24 +302,36 @@ silc_server_command_dup(SilcServerCommandContext ctx) 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'. */ + identifier `ident'. If there already exists pending command for the + specified command, ident, callback and context this function has no + effect. */ -void silc_server_command_pending(SilcServer server, +bool silc_server_command_pending(SilcServer server, SilcCommand reply_cmd, uint16 ident, - SilcServerPendingDestructor destructor, SilcCommandCb callback, void *context) { SilcServerCommandPending *reply; + /* Check whether identical pending already exists for same command, + ident, callback and callback context. If it does then it would be + error to register it again. */ + silc_dlist_start(server->pending_commands); + while ((reply = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) { + if (reply->reply_cmd == reply_cmd && reply->ident == ident && + reply->callback == callback && reply->context == context) + return FALSE; + } + reply = silc_calloc(1, sizeof(*reply)); reply->reply_cmd = reply_cmd; reply->ident = ident; reply->context = context; reply->callback = callback; - reply->destructor = destructor; silc_dlist_add(server->pending_commands, reply); + + return TRUE; } /* Deletes pending command by reply command type. */ @@ -354,7 +372,6 @@ silc_server_command_pending_check(SilcServer server, 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; i++; } @@ -364,14 +381,6 @@ silc_server_command_pending_check(SilcServer server, return callbacks; } -/* Destructor function for pending callbacks. This is called when using - pending commands to free the context given for the pending command. */ - -static void silc_server_command_destructor(void *context) -{ - silc_server_command_free((SilcServerCommandContext)context); -} - /* Sends simple status message as command reply packet */ static void @@ -401,7 +410,7 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd, SilcCommand command, SilcCommandStatus status, uint32 arg_type, - unsigned char *arg, + const unsigned char *arg, uint32 arg_len) { SilcBuffer buffer; @@ -438,8 +447,16 @@ silc_server_command_pending_error_check(SilcServerCommandContext cmd, status != SILC_STATUS_LIST_START && status != SILC_STATUS_LIST_ITEM && status != SILC_STATUS_LIST_END) { - /* Send the error message */ - silc_server_command_send_status_reply(cmd, command, status); + SilcBuffer buffer; + + /* Send the same command reply payload */ + silc_command_set_ident(cmdr->payload, + silc_command_get_ident(cmd->payload)); + buffer = silc_command_payload_encode_payload(cmdr->payload); + silc_server_packet_send(cmd->server, cmd->sock, + SILC_PACKET_COMMAND_REPLY, 0, + buffer->data, buffer->len, FALSE); + silc_buffer_free(buffer); return TRUE; } @@ -486,6 +503,8 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd, (*client_id)[0] = silc_id_payload_parse_id(tmp, len); if ((*client_id)[0] == NULL) { silc_free(*client_id); + silc_server_command_send_status_reply(cmd, command, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); return FALSE; } *client_id_count = 1; @@ -503,6 +522,9 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd, for (i = 0; i < *client_id_count; i++) silc_free((*client_id)[i]); silc_free(*client_id); + silc_server_command_send_status_reply( + cmd, command, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); return FALSE; } (*client_id_count)++; @@ -544,14 +566,27 @@ silc_server_command_whois_check(SilcServerCommandContext cmd, int i, k; bool no_res = TRUE; + SILC_LOG_DEBUG(("Start")); + for (i = 0; i < clients_count; i++) { entry = clients[i]; - - if (!entry || (entry->nickname && entry->username && entry->userinfo) || - !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED) || - !entry->router) + if (!entry) continue; + if ((entry->nickname && entry->username && entry->userinfo) || + !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) { + if (!entry->router) + continue; + + /* If we are normal server, and we've not resolved this client from + router and it is global client, we'll check whether it is on some + channel. If not then we cannot be sure about its validity, and + we'll resolve it from router. */ + if (cmd->server->server_type != SILC_SERVER || cmd->pending || + entry->connection || silc_hash_table_count(entry->channels)) + continue; + } + /* We need to resolve this entry since it is not complete */ if (!cmd->pending && entry->data.status & SILC_IDLIST_STATUS_RESOLVING) { @@ -559,7 +594,6 @@ silc_server_command_whois_check(SilcServerCommandContext cmd, 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_dup(cmd)); no_res = FALSE; @@ -642,7 +676,6 @@ silc_server_command_whois_check(SilcServerCommandContext cmd, /* 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; @@ -664,11 +697,13 @@ static void silc_server_command_whois_send_reply(SilcServerCommandContext cmd, SilcClientEntry *clients, uint32 clients_count, - int count) + int count, + const char *nickname, + SilcClientID **client_ids) { SilcServer server = cmd->server; char *tmp; - int i, k, len; + int i, k, len, valid_count; SilcBuffer packet, idp, channels; SilcClientEntry entry; SilcCommandStatus status; @@ -678,71 +713,49 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd, unsigned char *fingerprint; SilcSocketConnection hsock; - len = 0; - for (i = 0; i < clients_count; i++) + /* Process only valid clients and ignore those that are not registered. */ + valid_count = 0; + for (i = 0; i < clients_count; i++) { if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED) - len++; + valid_count++; + else + clients[i] = NULL; + } - if (len == 0 && clients_count) { - entry = clients[0]; - if (entry->nickname) { + if (!valid_count) { + /* No valid clients found, send error reply */ + if (nickname) { silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS, SILC_STATUS_ERR_NO_SUCH_NICK, - 3, entry->nickname, - strlen(entry->nickname)); - } else { - SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT); + 3, nickname, strlen(nickname)); + } else if (client_ids && client_ids[0]) { + SilcBuffer idp = silc_id_payload_encode(client_ids[0], SILC_ID_CLIENT); silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS, SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 2, idp->data, idp->len); silc_buffer_free(idp); } - return; } - status = SILC_STATUS_OK; - if (len > 1) + /* Start processing found clients. */ + if (valid_count > 1) status = SILC_STATUS_LIST_START; + else + status = SILC_STATUS_OK; for (i = 0, k = 0; i < clients_count; i++) { entry = clients[i]; - - 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, - SILC_STATUS_ERR_NO_SUCH_NICK, - 3, entry->nickname, - strlen(entry->nickname)); - } else { - SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT); - silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS, - SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, - 2, idp->data, idp->len); - silc_buffer_free(idp); - } - } + if (!entry) continue; - } if (k >= 1) status = SILC_STATUS_LIST_ITEM; - - if (clients_count > 1 && k == clients_count - 1) + if (valid_count > 1 && k == valid_count - 1) status = SILC_STATUS_LIST_END; - if (count && k - 1 == count) status = SILC_STATUS_LIST_END; - if (count && k - 1 > count) - break; - - /* Sanity check, however these should never fail. However, as - this sanity check has been added here they have failed. */ - if (!entry->nickname || !entry->username || !entry->userinfo) - continue; - /* Send WHOIS reply */ idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT); tmp = silc_argument_get_first_arg(cmd->args, NULL); @@ -812,6 +825,33 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd, } } +static void +silc_server_command_whois_send_router(SilcServerCommandContext cmd) +{ + SilcServer server = cmd->server; + SilcBuffer tmpbuf; + uint16 old_ident; + + old_ident = silc_command_get_ident(cmd->payload); + silc_command_set_ident(cmd->payload, ++server->cmd_ident); + tmpbuf = silc_command_payload_encode_payload(cmd->payload); + + /* Send WHOIS command to our router */ + silc_server_packet_send(server, (SilcSocketConnection) + server->router->connection, + SILC_PACKET_COMMAND, cmd->packet->flags, + tmpbuf->data, tmpbuf->len, TRUE); + + /* Reprocess this packet after received reply from router */ + silc_server_command_pending(server, SILC_COMMAND_WHOIS, + silc_command_get_ident(cmd->payload), + silc_server_command_whois, + silc_server_command_dup(cmd)); + cmd->pending = TRUE; + silc_command_set_ident(cmd->payload, old_ident); + silc_buffer_free(tmpbuf); +} + static int silc_server_command_whois_process(SilcServerCommandContext cmd) { @@ -824,55 +864,29 @@ silc_server_command_whois_process(SilcServerCommandContext cmd) int i, ret = 0; bool check_global = FALSE; - /* Protocol dictates that we must always send the received WHOIS request - to our router if we are normal server, so let's do it now unless we - are standalone. We will not send any replies to the client until we - have received reply from the router. */ - if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && - server->server_type == SILC_SERVER && !cmd->pending && - !server->standalone) { - SilcBuffer tmpbuf; - uint16 old_ident; - - old_ident = silc_command_get_ident(cmd->payload); - silc_command_set_ident(cmd->payload, ++server->cmd_ident); - tmpbuf = silc_command_payload_encode_payload(cmd->payload); - - /* Send WHOIS command to our router */ - silc_server_packet_send(server, (SilcSocketConnection) - server->router->connection, - SILC_PACKET_COMMAND, cmd->packet->flags, - tmpbuf->data, tmpbuf->len, TRUE); - - /* Reprocess this packet after received reply from router */ - silc_server_command_pending(server, SILC_COMMAND_WHOIS, - silc_command_get_ident(cmd->payload), - silc_server_command_destructor, - silc_server_command_whois, - silc_server_command_dup(cmd)); - cmd->pending = TRUE; - - silc_command_set_ident(cmd->payload, old_ident); + /* Parse the whois request */ + if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count, + &nick, &server_name, &count, + SILC_COMMAND_WHOIS)) + return 0; - silc_buffer_free(tmpbuf); + /* Send the WHOIS request to the router only if it included nickname. + Since nicknames can be expanded into many clients we need to send it + to router. If the WHOIS included only client ID's we will check them + first locally since we just might have them. */ + if (nick && !client_id_count && cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && + server->server_type == SILC_SERVER && !cmd->pending && + !server->standalone) { + silc_server_command_whois_send_router(cmd); ret = -1; goto out; } - /* We are ready to process the command request. Let's search for the - requested client and send reply to the requesting client. */ - if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) check_global = TRUE; else if (server->server_type != SILC_SERVER) check_global = TRUE; - /* Parse the whois request */ - if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count, - &nick, &server_name, &count, - SILC_COMMAND_WHOIS)) - return 0; - /* Get all clients matching that ID or nickname from local list */ if (client_id_count) { /* Check all Client ID's received in the command packet */ @@ -886,9 +900,20 @@ silc_server_command_whois_process(SilcServerCommandContext cmd) clients = silc_realloc(clients, sizeof(*clients) * (clients_count + 1)); clients[clients_count++] = entry; + } else { + /* If we are normal server and did not send the request first to router + do it now, since we do not have the Client ID information. */ + if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && + server->server_type == SILC_SERVER && !cmd->pending && + !server->standalone) { + silc_server_command_whois_send_router(cmd); + ret = -1; + goto out; + } } } } else { + /* Find by nickname */ if (!silc_idlist_get_clients_by_hash(server->local_list, nick, server->md5hash, &clients, &clients_count)) @@ -906,6 +931,16 @@ silc_server_command_whois_process(SilcServerCommandContext cmd) } if (!clients) { + /* If we are normal server and did not send the request first to router + do it now, since we do not have the information. */ + if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && + server->server_type == SILC_SERVER && !cmd->pending && + !server->standalone) { + silc_server_command_whois_send_router(cmd); + ret = -1; + goto out; + } + /* Such client(s) really does not exist in the SILC network. */ if (!client_id_count) { silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS, @@ -933,7 +968,7 @@ silc_server_command_whois_process(SilcServerCommandContext cmd) /* Send the command reply */ silc_server_command_whois_send_reply(cmd, clients, clients_count, - count); + count, nick, client_id); out: if (client_id_count) { @@ -959,9 +994,7 @@ SILC_SERVER_CMD_FUNC(whois) SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOIS, cmd, 1, 3328); ret = silc_server_command_whois_process(cmd); - - if (!ret) - silc_server_command_free(cmd); + silc_server_command_free(cmd); } /****************************************************************************** @@ -1030,11 +1063,9 @@ silc_server_command_whowas_check(SilcServerCommandContext cmd, /* Reprocess this packet after received reply */ silc_server_command_pending(server, SILC_COMMAND_WHOWAS, silc_command_get_ident(cmd->payload), - silc_server_command_destructor, silc_server_command_whowas, silc_server_command_dup(cmd)); cmd->pending = TRUE; - silc_command_set_ident(cmd->payload, old_ident); silc_buffer_free(tmpbuf); @@ -1052,49 +1083,57 @@ silc_server_command_whowas_send_reply(SilcServerCommandContext cmd, { SilcServer server = cmd->server; char *tmp; - int i, count = 0, len; + int i, k, count = 0, len; SilcBuffer packet, idp; SilcClientEntry entry = NULL; SilcCommandStatus status; uint16 ident = silc_command_get_ident(cmd->payload); - char found = FALSE; char nh[256], uh[256]; + int valid_count; status = SILC_STATUS_OK; - if (clients_count > 1) - status = SILC_STATUS_LIST_START; + /* Process only entries that are not registered anymore. */ + valid_count = 0; for (i = 0; i < clients_count; i++) { - entry = clients[i]; + if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED) + clients[i] = NULL; + else + valid_count++; + } - /* 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.status & SILC_IDLIST_STATUS_REGISTERED) - continue; - if (entry->id == NULL) - continue; + if (!valid_count) { + /* No valid entries found at all, just send error */ + unsigned char *tmp; + + tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); + if (tmp) + silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS, + SILC_STATUS_ERR_NO_SUCH_NICK, + 3, tmp, strlen(tmp)); + return; + } - if (count && i - 1 == count) - break; + if (valid_count > 1) + status = SILC_STATUS_LIST_START; - found = TRUE; + for (i = 0, k = 0; i < clients_count; i++) { + entry = clients[i]; + if (!entry) + continue; - if (clients_count > 2) + if (k >= 1) status = SILC_STATUS_LIST_ITEM; - - if (clients_count > 1 && i == clients_count - 1) + if (valid_count > 1 && k == valid_count - 1) status = SILC_STATUS_LIST_END; + if (count && k - 1 == count) + status = SILC_STATUS_LIST_END; + if (count && k - 1 > count) + break; - /* Sanity check, however these should never fail. However, as - this sanity check has been added here they have failed. */ - if (!entry->nickname || !entry->username) - continue; - /* Send WHOWAS reply */ idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT); tmp = silc_argument_get_first_arg(cmd->args, NULL); - memset(uh, 0, sizeof(uh)); memset(nh, 0, sizeof(nh)); @@ -1117,35 +1156,23 @@ silc_server_command_whowas_send_reply(SilcServerCommandContext cmd, strcat(uh, "*private*"); } - if (entry->userinfo) - packet = - silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS, - status, ident, 4, - 2, idp->data, idp->len, - 3, nh, strlen(nh), - 4, uh, strlen(uh), - 5, entry->userinfo, - strlen(entry->userinfo)); - else - packet = - silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS, - status, ident, 3, - 2, idp->data, idp->len, - 3, nh, strlen(nh), - 4, uh, strlen(uh)); - + packet = + silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS, + status, ident, 4, + 2, idp->data, idp->len, + 3, nh, strlen(nh), + 4, uh, strlen(uh), + 5, entry->userinfo, + entry->userinfo ? + strlen(entry->userinfo) : 0); silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, packet->data, packet->len, FALSE); silc_buffer_free(packet); silc_buffer_free(idp); - } - if (found == FALSE && entry) - silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS, - SILC_STATUS_ERR_NO_SUCH_NICK, - 3, entry->nickname, - strlen(entry->nickname)); + k++; + } } static int @@ -1182,11 +1209,9 @@ silc_server_command_whowas_process(SilcServerCommandContext cmd) /* Reprocess this packet after received reply from router */ silc_server_command_pending(server, SILC_COMMAND_WHOWAS, silc_command_get_ident(cmd->payload), - silc_server_command_destructor, silc_server_command_whowas, silc_server_command_dup(cmd)); cmd->pending = TRUE; - silc_command_set_ident(cmd->payload, old_ident); silc_buffer_free(tmpbuf); @@ -1244,7 +1269,6 @@ silc_server_command_whowas_process(SilcServerCommandContext cmd) silc_free(clients); silc_free(nick); silc_free(server_name); - return ret; } @@ -1258,9 +1282,7 @@ SILC_SERVER_CMD_FUNC(whowas) SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2); ret = silc_server_command_whowas_process(cmd); - - if (!ret) - silc_server_command_free(cmd); + silc_server_command_free(cmd); } /****************************************************************************** @@ -1269,7 +1291,34 @@ SILC_SERVER_CMD_FUNC(whowas) ******************************************************************************/ -static bool +static void +silc_server_command_identify_send_router(SilcServerCommandContext cmd) +{ + SilcServer server = cmd->server; + SilcBuffer tmpbuf; + uint16 old_ident; + + old_ident = silc_command_get_ident(cmd->payload); + silc_command_set_ident(cmd->payload, ++server->cmd_ident); + tmpbuf = silc_command_payload_encode_payload(cmd->payload); + + /* Send IDENTIFY command to our router */ + silc_server_packet_send(server, (SilcSocketConnection) + server->router->connection, + SILC_PACKET_COMMAND, cmd->packet->flags, + tmpbuf->data, tmpbuf->len, TRUE); + + /* Reprocess this packet after received reply from router */ + silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, + silc_command_get_ident(cmd->payload), + silc_server_command_identify, + silc_server_command_dup(cmd)); + cmd->pending = TRUE; + silc_command_set_ident(cmd->payload, old_ident); + silc_buffer_free(tmpbuf); +} + +static int silc_server_command_identify_parse(SilcServerCommandContext cmd, SilcClientEntry **clients, uint32 *clients_count, @@ -1299,6 +1348,15 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd, if (!tmp) { /* No ID, get the names. */ + /* If we are normal server and have not resolved information from + router yet, do so now. */ + if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && + server->server_type == SILC_SERVER && !cmd->pending && + !server->standalone) { + silc_server_command_identify_send_router(cmd); + return -1; + } + /* Try to get nickname@server. */ tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); if (tmp) { @@ -1326,10 +1384,11 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd, silc_free(nick_server); if (!(*clients)) { + /* the nickname does not exist, send error reply */ silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY, SILC_STATUS_ERR_NO_SUCH_NICK, 3, tmp, strlen(tmp)); - return FALSE; + return 0; } } @@ -1348,10 +1407,11 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd, } if (!(*servers)) { + /* the server does not exist, send error reply */ silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY, SILC_STATUS_ERR_NO_SUCH_SERVER, 3, tmp, strlen(tmp)); - return FALSE; + return 0; } } @@ -1370,17 +1430,18 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd, } if (!(*channels)) { + /* The channel does not exist, send error reply */ silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY, SILC_STATUS_ERR_NO_SUCH_CHANNEL, 3, tmp, strlen(tmp)); - return FALSE; + return 0; } } if (!(*clients) && !(*servers) && !(*channels)) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY, SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); - return FALSE; + return 0; } } else { /* Command includes ID, we must use that. Also check whether the command @@ -1394,14 +1455,15 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd, if (!tmp) continue; - idp = silc_id_payload_parse_data(tmp, len); + idp = silc_id_payload_parse(tmp, len); if (!idp) { silc_free(*clients); silc_free(*servers); silc_free(*channels); - silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY, - SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); - return FALSE; + silc_server_command_send_status_reply( + cmd, SILC_COMMAND_IDENTIFY, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + return 0; } id = silc_id_payload_get_id(idp); @@ -1419,10 +1481,23 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd, (*clients_count + 1)); (*clients)[(*clients_count)++] = (SilcClientEntry)entry; } else { - silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY, + /* If we are normal server and have not resolved information from + router yet, do so now. */ + if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && + server->server_type == SILC_SERVER && !cmd->pending && + !server->standalone) { + silc_server_command_identify_send_router(cmd); + silc_free(*clients); + silc_free(*servers); + silc_free(*channels); + return -1; + } else { + silc_server_command_send_status_data( + cmd, SILC_COMMAND_IDENTIFY, SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, - 2, tmp, len); - error = TRUE; + 2, tmp, len); + error = TRUE; + } } break; @@ -1438,10 +1513,23 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd, (*servers_count + 1)); (*servers)[(*servers_count)++] = (SilcServerEntry)entry; } else { - silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY, - SILC_STATUS_ERR_NO_SUCH_SERVER_ID, - 2, tmp, len); - error = TRUE; + /* If we are normal server and have not resolved information from + router yet, do so now. */ + if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && + server->server_type == SILC_SERVER && !cmd->pending && + !server->standalone) { + silc_server_command_identify_send_router(cmd); + silc_free(*clients); + silc_free(*servers); + silc_free(*channels); + return -1; + } else { + silc_server_command_send_status_data( + cmd, SILC_COMMAND_IDENTIFY, + SILC_STATUS_ERR_NO_SUCH_SERVER_ID, + 2, tmp, len); + error = TRUE; + } } break; @@ -1456,10 +1544,23 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd, (*channels_count + 1)); (*channels)[(*channels_count)++] = (SilcChannelEntry)entry; } else { - silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY, - SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID, - 2, tmp, len); - error = TRUE; + /* If we are normal server and have not resolved information from + router yet, do so now. */ + if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && + server->server_type == SILC_SERVER && !cmd->pending && + !server->standalone) { + silc_server_command_identify_send_router(cmd); + silc_free(*clients); + silc_free(*servers); + silc_free(*channels); + return -1; + } else { + silc_server_command_send_status_data( + cmd, SILC_COMMAND_IDENTIFY, + SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID, + 2, tmp, len); + error = TRUE; + } } break; } @@ -1482,7 +1583,7 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd, else *count = 0; - return TRUE; + return 1; } /* Checks that all mandatory fields in client entry are present. If not @@ -1503,12 +1604,23 @@ silc_server_command_identify_check_client(SilcServerCommandContext cmd, for (i = 0; i < clients_count; i++) { entry = clients[i]; - - if (!entry || entry->nickname || - !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED) || - !entry->router) + if (!entry) continue; + if (entry->nickname || + !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) { + if (!entry->router) + continue; + + /* If we are normal server, and we've not resolved this client from + router and it is global client, we'll check whether it is on some + channel. If not then we cannot be sure about its validity, and + we'll resolve it from router. */ + if (cmd->server->server_type != SILC_SERVER || cmd->pending || + entry->connection || silc_hash_table_count(entry->channels)) + continue; + } + /* We need to resolve this entry since it is not complete */ if (!cmd->pending && entry->data.status & SILC_IDLIST_STATUS_RESOLVING) { @@ -1516,7 +1628,6 @@ silc_server_command_identify_check_client(SilcServerCommandContext cmd, 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)); no_res = FALSE; @@ -1599,7 +1710,6 @@ silc_server_command_identify_check_client(SilcServerCommandContext cmd, /* 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; @@ -1628,7 +1738,7 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd, int count) { SilcServer server = cmd->server; - int i, k, len; + int i, k, len, valid_count; SilcBuffer packet, idp; SilcCommandStatus status; uint16 ident = silc_command_get_ident(cmd->payload); @@ -1640,62 +1750,58 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd, if (clients) { SilcClientEntry entry; - len = 0; - for (i = 0; i < clients_count; i++) + /* Process only valid entries. */ + valid_count = 0; + for (i = 0; i < clients_count; i++) { if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED) - len++; + valid_count++; + else + clients[i] = NULL; + } + + if (!valid_count) { + /* No valid entries found at all, just send error */ + unsigned char *tmp; - if (len == 0 && clients_count) { - entry = clients[0]; - if (entry->nickname) { + tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); + if (tmp) { silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY, SILC_STATUS_ERR_NO_SUCH_NICK, - 3, entry->nickname, - strlen(entry->nickname)); + 3, tmp, strlen(tmp)); } else { - SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT); + tmp = silc_argument_get_arg_type(cmd->args, 5, (uint32 *)&len); silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY, SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, - 2, idp->data, idp->len); - silc_buffer_free(idp); + 2, tmp, len); } - return; } - if (len > 1) + /* Process all valid client entries and send command replies */ + + if (valid_count > 1) status = SILC_STATUS_LIST_START; for (i = 0, k = 0; i < clients_count; i++) { entry = clients[i]; - - 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, - SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, - 2, idp->data, idp->len); - silc_buffer_free(idp); - } + if (!entry) continue; - } - + if (k >= 1) status = SILC_STATUS_LIST_ITEM; - if (clients_count > 1 && k == clients_count - 1 + if (valid_count > 1 && k == valid_count - 1 && !servers_count && !channels_count) status = SILC_STATUS_LIST_END; if (count && k - 1 == count) status = SILC_STATUS_LIST_END; if (count && k - 1 > count) break; - + /* Send IDENTIFY reply */ + idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT); - memset(uh, 0, sizeof(uh)); memset(nh, 0, sizeof(nh)); - strncat(nh, entry->nickname, strlen(entry->nickname)); if (!strchr(entry->nickname, '@')) { strncat(nh, "@", 1); @@ -1708,7 +1814,7 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd, server->server_name, len); } } - + if (!entry->username) { packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY, status, ident, 2, @@ -1740,9 +1846,6 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd, } } - status = (status == SILC_STATUS_LIST_ITEM ? - SILC_STATUS_LIST_ITEM : SILC_STATUS_OK); - if (servers) { SilcServerEntry entry; @@ -1763,19 +1866,13 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd, /* Send IDENTIFY reply */ idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER); - if (entry->server_name) { - packet = - silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY, - status, ident, 2, - 2, idp->data, idp->len, - 3, entry->server_name, - strlen(entry->server_name)); - } else { - packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY, - status, ident, 1, - 2, idp->data, idp->len); - } - + packet = + silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY, + status, ident, 2, + 2, idp->data, idp->len, + 3, entry->server_name, + entry->server_name ? + strlen(entry->server_name) : 0); silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, packet->data, packet->len, FALSE); @@ -1786,9 +1883,6 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd, } } - status = (status == SILC_STATUS_LIST_ITEM ? - SILC_STATUS_LIST_ITEM : SILC_STATUS_OK); - if (channels) { SilcChannelEntry entry; @@ -1809,19 +1903,13 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd, /* Send IDENTIFY reply */ idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL); - if (entry->channel_name) { - packet = - silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY, - status, ident, 2, - 2, idp->data, idp->len, - 3, entry->channel_name, - strlen(entry->channel_name)); - } else { - packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY, - status, ident, 1, - 2, idp->data, idp->len); - } - + packet = + silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY, + status, ident, 2, + 2, idp->data, idp->len, + 3, entry->channel_name, + entry->channel_name ? + strlen(entry->channel_name): 0); silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, packet->data, packet->len, FALSE); @@ -1836,7 +1924,6 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd, static int silc_server_command_identify_process(SilcServerCommandContext cmd) { - SilcServer server = cmd->server; uint32 count = 0; int ret = 0; SilcClientEntry *clients = NULL; @@ -1844,51 +1931,15 @@ silc_server_command_identify_process(SilcServerCommandContext cmd) SilcChannelEntry *channels = NULL; uint32 clients_count = 0, servers_count = 0, channels_count = 0; - /* Protocol dictates that we must always send the received IDENTIFY request - to our router if we are normal server, so let's do it now unless we - are standalone. We will not send any replies to the client until we - have received reply from the router. */ - if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && - server->server_type == SILC_SERVER && !cmd->pending && - !server->standalone) { - SilcBuffer tmpbuf; - uint16 old_ident; - - old_ident = silc_command_get_ident(cmd->payload); - silc_command_set_ident(cmd->payload, ++server->cmd_ident); - tmpbuf = silc_command_payload_encode_payload(cmd->payload); - - /* Send IDENTIFY command to our router */ - silc_server_packet_send(server, (SilcSocketConnection) - server->router->connection, - SILC_PACKET_COMMAND, cmd->packet->flags, - tmpbuf->data, tmpbuf->len, TRUE); - - /* Reprocess this packet after received reply from router */ - silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, - silc_command_get_ident(cmd->payload), - silc_server_command_destructor, - silc_server_command_identify, - silc_server_command_dup(cmd)); - cmd->pending = TRUE; - - silc_command_set_ident(cmd->payload, old_ident); - - silc_buffer_free(tmpbuf); - ret = -1; - goto out; - } - - /* We are ready to process the command request. Let's search for the - requested client and send reply to the requesting client. */ - /* Parse the IDENTIFY request */ - if (!silc_server_command_identify_parse(cmd, - &clients, &clients_count, - &servers, &servers_count, - &channels, &channels_count, - &count)) - return 0; + ret = silc_server_command_identify_parse(cmd, + &clients, &clients_count, + &servers, &servers_count, + &channels, &channels_count, + &count); + if (ret < 1) + return ret; + ret = 0; /* Check that all mandatory fields are present and request those data from the server who owns the client if necessary. */ @@ -1909,7 +1960,6 @@ silc_server_command_identify_process(SilcServerCommandContext cmd) silc_free(clients); silc_free(servers); silc_free(channels); - return ret; } @@ -1921,28 +1971,7 @@ SILC_SERVER_CMD_FUNC(identify) SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 3328); ret = silc_server_command_identify_process(cmd); - - if (!ret) - silc_server_command_free(cmd); -} - -/* Checks string for bad characters and returns TRUE if they are found. */ - -static int silc_server_command_bad_chars(char *nick) -{ - int i; - - for (i = 0; i < strlen(nick); i++) { - if (!isascii(nick[i])) - return TRUE; - if (nick[i] <= 32) return TRUE; - if (nick[i] == ' ') return TRUE; - if (nick[i] == '*') return TRUE; - if (nick[i] == '?') return TRUE; - if (nick[i] == ',') return TRUE; - } - - return FALSE; + silc_server_command_free(cmd); } /* Server side of command NICK. Sets nickname for user. Setting @@ -1956,6 +1985,7 @@ SILC_SERVER_CMD_FUNC(nick) SilcServer server = cmd->server; SilcBuffer packet, nidp, oidp = NULL; SilcClientID *new_id; + uint32 nick_len; char *nick; uint16 ident = silc_command_get_ident(cmd->payload); int nickfail = 0; @@ -1966,16 +1996,15 @@ SILC_SERVER_CMD_FUNC(nick) SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_NICK, cmd, 1, 1); /* Check nickname */ - nick = silc_argument_get_arg_type(cmd->args, 1, NULL); - if (silc_server_command_bad_chars(nick) == TRUE) { + nick = silc_argument_get_arg_type(cmd->args, 1, &nick_len); + if (nick_len > 128) + nick[128] = '\0'; + if (silc_server_name_bad_chars(nick, nick_len) == TRUE) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK, SILC_STATUS_ERR_BAD_NICKNAME); goto out; } - if (strlen(nick) > 128) - nick[128] = '\0'; - /* Check for same nickname */ if (!strcmp(client->nickname, nick)) { nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); @@ -2016,7 +2045,7 @@ SILC_SERVER_CMD_FUNC(nick) /* Update client cache */ silc_idcache_add(server->local_list->clients, client->nickname, - client->id, (void *)client, FALSE); + client->id, (void *)client, 0, NULL); nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); @@ -2052,7 +2081,7 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd, SilcChannelEntry *gch, uint32 gch_count) { - int i; + int i, k; SilcBuffer packet, idp; SilcChannelEntry entry; SilcCommandStatus status; @@ -2060,29 +2089,34 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd, char *topic; unsigned char usercount[4]; uint32 users; + int valid_lcount = 0, valid_rcount = 0; - for (i = 0; i < lch_count; i++) + for (i = 0; i < lch_count; i++) { if (lch[i]->mode & SILC_CHANNEL_MODE_SECRET) lch[i] = NULL; - for (i = 0; i < gch_count; i++) + else + valid_lcount++; + } + for (i = 0; i < gch_count; i++) { if (gch[i]->mode & SILC_CHANNEL_MODE_SECRET) gch[i] = NULL; + else + valid_rcount++; + } status = SILC_STATUS_OK; if ((lch_count + gch_count) > 1) status = SILC_STATUS_LIST_START; /* Local list */ - for (i = 0; i < lch_count; i++) { + for (i = 0, k = 0; i < lch_count; i++) { entry = lch[i]; - if (!entry) continue; - if (i >= 1) + if (k >= 1) status = SILC_STATUS_LIST_ITEM; - - if (lch_count > 1 && i == lch_count - 1 && !gch_count) + if (valid_lcount > 1 && k == valid_lcount - 1 && !valid_rcount) status = SILC_STATUS_LIST_END; idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL); @@ -2097,43 +2131,31 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd, } /* Send the reply */ - if (topic) - packet = - silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, - status, ident, 4, - 2, idp->data, idp->len, - 3, entry->channel_name, - strlen(entry->channel_name), - 4, topic, strlen(topic), - 5, usercount, 4); - else - packet = - silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, - status, ident, 3, - 2, idp->data, idp->len, - 3, entry->channel_name, - strlen(entry->channel_name), - 5, usercount, 4); + packet = + silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, + status, ident, 4, + 2, idp->data, idp->len, + 3, entry->channel_name, + strlen(entry->channel_name), + 4, topic, topic ? strlen(topic) : 0, + 5, usercount, 4); silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, packet->data, packet->len, FALSE); silc_buffer_free(packet); silc_buffer_free(idp); + k++; } - status = i ? SILC_STATUS_LIST_ITEM : SILC_STATUS_OK; - /* Global list */ - for (i = 0; i < gch_count; i++) { + for (i = 0, k = 0; i < gch_count; i++) { entry = gch[i]; - if (!entry) continue; - if (i >= 1) + if (k >= 1) status = SILC_STATUS_LIST_ITEM; - - if (gch_count > 1 && i == lch_count - 1) + if (valid_rcount > 1 && k == valid_rcount - 1) status = SILC_STATUS_LIST_END; idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL); @@ -2148,28 +2170,20 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd, } /* Send the reply */ - if (topic) - packet = - silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, - status, ident, 4, - 2, idp->data, idp->len, - 3, entry->channel_name, - strlen(entry->channel_name), - 4, topic, strlen(topic), - 5, usercount, 4); - else - packet = - silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, - status, ident, 3, - 2, idp->data, idp->len, - 3, entry->channel_name, - strlen(entry->channel_name), - 5, usercount, 4); + packet = + silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, + status, ident, 4, + 2, idp->data, idp->len, + 3, entry->channel_name, + strlen(entry->channel_name), + 4, topic, topic ? strlen(topic) : 0, + 5, usercount, 4); silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, packet->data, packet->len, FALSE); silc_buffer_free(packet); silc_buffer_free(idp); + k++; } } @@ -2205,13 +2219,12 @@ SILC_SERVER_CMD_FUNC(list) /* Reprocess this packet after received reply from router */ silc_server_command_pending(server, SILC_COMMAND_LIST, silc_command_get_ident(cmd->payload), - silc_server_command_destructor, silc_server_command_list, silc_server_command_dup(cmd)); cmd->pending = TRUE; silc_command_set_ident(cmd->payload, old_ident); silc_buffer_free(tmpbuf); - return; + goto out; } /* Get Channel ID */ @@ -2237,6 +2250,9 @@ SILC_SERVER_CMD_FUNC(list) silc_server_command_list_send_reply(cmd, lchannels, lch_count, gchannels, gch_count); + silc_free(lchannels); + silc_free(gchannels); + out: silc_server_command_free(cmd); } @@ -2327,13 +2343,14 @@ SILC_SERVER_CMD_FUNC(topic) if (!server->standalone) silc_server_send_notify_topic_set(server, server->router->connection, server->server_type == SILC_ROUTER ? - TRUE : FALSE, channel, client->id, + TRUE : FALSE, channel, + client->id, SILC_ID_CLIENT, channel->topic); idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); /* Send notify about topic change to all clients on the channel */ - silc_server_send_notify_to_channel(server, NULL, channel, TRUE, + silc_server_send_notify_to_channel(server, NULL, channel, FALSE, SILC_NOTIFY_TYPE_TOPIC_SET, 2, idp->data, idp->len, channel->topic, strlen(channel->topic)); @@ -2342,16 +2359,12 @@ SILC_SERVER_CMD_FUNC(topic) /* Send the topic to client as reply packet */ idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL); - if (channel->topic) - packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, - SILC_STATUS_OK, ident, 2, - 2, idp->data, idp->len, - 3, channel->topic, - strlen(channel->topic)); - else - packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, - SILC_STATUS_OK, ident, 1, - 2, idp->data, idp->len); + packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, + SILC_STATUS_OK, ident, 2, + 2, idp->data, idp->len, + 3, channel->topic, + channel->topic ? + strlen(channel->topic) : 0); silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, packet->data, packet->len, FALSE); @@ -2434,6 +2447,7 @@ SILC_SERVER_CMD_FUNC(invite) tmp = silc_argument_get_arg_type(cmd->args, 2, &len); if (tmp) { char invite[512]; + bool resolve; dest_id = silc_id_payload_parse_id(tmp, len); if (!dest_id) { @@ -2443,11 +2457,12 @@ SILC_SERVER_CMD_FUNC(invite) } /* Get the client entry */ - dest = silc_server_get_client_resolve(server, dest_id); + dest = silc_server_get_client_resolve(server, dest_id, &resolve); if (!dest) { - if (server->server_type != SILC_SERVER) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE, - SILC_STATUS_ERR_NO_SUCH_CLIENT_ID); + if (server->server_type != SILC_SERVER || !resolve) { + silc_server_command_send_status_reply( + cmd, SILC_COMMAND_INVITE, + SILC_STATUS_ERR_NO_SUCH_CLIENT_ID); goto out; } @@ -2455,13 +2470,12 @@ SILC_SERVER_CMD_FUNC(invite) receiving the reply to the query. */ silc_server_command_pending(server, SILC_COMMAND_WHOIS, server->cmd_ident, - silc_server_command_destructor, silc_server_command_invite, silc_server_command_dup(cmd)); cmd->pending = TRUE; silc_free(channel_id); silc_free(dest_id); - return; + goto out; } /* Check whether the requested client is already on the channel. */ @@ -2661,6 +2675,7 @@ SILC_SERVER_CMD_FUNC(kill) SilcClientID *client_id; unsigned char *tmp, *comment; uint32 tmp_len, tmp_len2; + bool local; SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 2); @@ -2698,9 +2713,11 @@ SILC_SERVER_CMD_FUNC(kill) /* Get the client entry */ remote_client = silc_idlist_find_client_by_id(server->local_list, client_id, TRUE, NULL); + local = TRUE; if (!remote_client) { remote_client = silc_idlist_find_client_by_id(server->global_list, client_id, TRUE, NULL); + local = FALSE; if (!remote_client) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL, SILC_STATUS_ERR_NO_SUCH_CLIENT_ID); @@ -2754,9 +2771,17 @@ SILC_SERVER_CMD_FUNC(kill) silc_server_free_client_data(server, sock, remote_client, FALSE, NULL); silc_server_close_connection(server, sock); } else { + /* Update statistics */ + if (remote_client->connection) + server->stat.my_clients--; + if (server->server_type == SILC_ROUTER) + server->stat.cell_clients--; + SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR); + SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR); + /* Remove remote client */ - if (!silc_idlist_del_client(server->global_list, remote_client)) - silc_idlist_del_client(server->local_list, remote_client); + silc_idlist_del_client(local ? server->local_list : + server->global_list, remote_client); } out: @@ -2824,10 +2849,10 @@ SILC_SERVER_CMD_FUNC(info) memset(info_string, 0, sizeof(info_string)); snprintf(info_string, sizeof(info_string), "location: %s server: %s admin: %s <%s>", - server->config->admin_info->location, - server->config->admin_info->server_type, - server->config->admin_info->admin_name, - server->config->admin_info->admin_email); + server->config->server_info->location, + server->config->server_info->server_type, + server->config->server_info->admin, + server->config->server_info->email); server_info = info_string; entry = server->id_entry; @@ -2859,13 +2884,12 @@ SILC_SERVER_CMD_FUNC(info) /* Reprocess this packet after received reply from router */ silc_server_command_pending(server, SILC_COMMAND_INFO, silc_command_get_ident(cmd->payload), - silc_server_command_destructor, silc_server_command_info, silc_server_command_dup(cmd)); cmd->pending = TRUE; silc_command_set_ident(cmd->payload, old_ident); silc_buffer_free(tmpbuf); - return; + goto out; } if (!entry && !cmd->pending && !server->standalone) { @@ -2884,13 +2908,12 @@ SILC_SERVER_CMD_FUNC(info) /* Reprocess this packet after received reply from router */ silc_server_command_pending(server, SILC_COMMAND_INFO, silc_command_get_ident(cmd->payload), - silc_server_command_destructor, silc_server_command_info, silc_server_command_dup(cmd)); cmd->pending = TRUE; silc_command_set_ident(cmd->payload, old_ident); silc_buffer_free(tmpbuf); - return; + goto out; } } @@ -2908,20 +2931,14 @@ SILC_SERVER_CMD_FUNC(info) server_name = entry->server_name; /* Send the reply */ - if (server_info) - packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO, - SILC_STATUS_OK, ident, 3, - 2, idp->data, idp->len, - 3, server_name, - strlen(server_name), - 4, server_info, - strlen(server_info)); - else - packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO, - SILC_STATUS_OK, ident, 2, - 2, idp->data, idp->len, - 3, server_name, - strlen(server_name)); + packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO, + SILC_STATUS_OK, ident, 3, + 2, idp->data, idp->len, + 3, server_name, + strlen(server_name), + 4, server_info, + server_info ? + strlen(server_info) : 0); silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, packet->data, packet->len, FALSE); @@ -2981,7 +2998,9 @@ static void silc_server_command_join_channel(SilcServer server, SilcClientID *client_id, bool created, bool create_key, - uint32 umode) + uint32 umode, + const unsigned char *auth, + uint32 auth_len) { SilcSocketConnection sock = cmd->sock; unsigned char *tmp; @@ -2992,6 +3011,8 @@ static void silc_server_command_join_channel(SilcServer server, SilcBuffer reply, chidp, clidp, keyp = NULL, user_list, mode_list; uint16 ident = silc_command_get_ident(cmd->payload); char check[512], check2[512]; + bool founder = FALSE; + bool resolve; SILC_LOG_DEBUG(("Start")); @@ -3002,102 +3023,137 @@ static void silc_server_command_join_channel(SilcServer server, if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) { client = (SilcClientEntry)sock->user_data; } else { - client = silc_server_get_client_resolve(server, client_id); + client = silc_server_get_client_resolve(server, client_id, &resolve); if (!client) { if (cmd->pending) goto out; + if (!resolve) { + silc_server_command_send_status_reply( + cmd, SILC_COMMAND_JOIN, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + /* The client info is being resolved. Reprocess this packet after receiving the reply to the query. */ silc_server_command_pending(server, SILC_COMMAND_WHOIS, - server->cmd_ident, NULL, + server->cmd_ident, silc_server_command_join, silc_server_command_dup(cmd)); cmd->pending = TRUE; - return; + goto out; } cmd->pending = FALSE; } /* - * Check channel modes + * Check founder auth payload if provided. If client can gain founder + * privileges it can override various conditions on joining the channel, + * and can have directly the founder mode set on the channel. */ - - memset(check, 0, sizeof(check)); - memset(check2, 0, sizeof(check2)); - strncat(check, client->nickname, strlen(client->nickname)); - strncat(check, "!", 1); - strncat(check, client->username, strlen(client->username)); - if (!strchr(client->username, '@')) { - strncat(check, "@", 1); - strncat(check, cmd->sock->hostname, strlen(cmd->sock->hostname)); + if (auth && auth_len && channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) { + SilcIDListData idata = (SilcIDListData)client; + + if (channel->founder_key && idata->public_key && + silc_pkcs_public_key_compare(channel->founder_key, + idata->public_key)) { + void *auth_data = (channel->founder_method == SILC_AUTH_PASSWORD ? + (void *)channel->founder_passwd : + (void *)channel->founder_key); + uint32 auth_data_len = (channel->founder_method == SILC_AUTH_PASSWORD ? + channel->founder_passwd_len : 0); + + /* Check whether the client is to become founder */ + if (silc_auth_verify_data(auth, auth_len, channel->founder_method, + auth_data, auth_data_len, + idata->hash, client->id, SILC_ID_CLIENT)) { + umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO); + founder = TRUE; + } + } } - strncat(check2, client->nickname, strlen(client->nickname)); - if (!strchr(client->nickname, '@')) { - strncat(check2, "@", 1); - strncat(check2, server->server_name, strlen(server->server_name)); - } - strncat(check2, "!", 1); - strncat(check2, client->username, strlen(client->username)); - if (!strchr(client->username, '@')) { - strncat(check2, "@", 1); - strncat(check2, cmd->sock->hostname, strlen(cmd->sock->hostname)); - } + /* + * Check channel modes + */ - /* Check invite list if channel is invite-only channel */ - if (channel->mode & SILC_CHANNEL_MODE_INVITE) { - if (!channel->invite_list || - (!silc_string_match(channel->invite_list, check) && - !silc_string_match(channel->invite_list, check2))) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, - SILC_STATUS_ERR_NOT_INVITED); - goto out; + if (!umode) { + memset(check, 0, sizeof(check)); + memset(check2, 0, sizeof(check2)); + strncat(check, client->nickname, strlen(client->nickname)); + strncat(check, "!", 1); + strncat(check, client->username, strlen(client->username)); + if (!strchr(client->username, '@')) { + strncat(check, "@", 1); + strncat(check, cmd->sock->hostname, strlen(cmd->sock->hostname)); } - } - /* Check ban list if it exists. If the client's nickname, server, - username and/or hostname is in the ban list the access to the - channel is denied. */ - if (channel->ban_list) { - if (!channel->ban_list || - silc_string_match(channel->ban_list, check) || - silc_string_match(channel->ban_list, check2)) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, - SILC_STATUS_ERR_BANNED_FROM_CHANNEL); - goto out; + strncat(check2, client->nickname, strlen(client->nickname)); + if (!strchr(client->nickname, '@')) { + strncat(check2, "@", 1); + strncat(check2, server->server_name, strlen(server->server_name)); + } + strncat(check2, "!", 1); + strncat(check2, client->username, strlen(client->username)); + if (!strchr(client->username, '@')) { + strncat(check2, "@", 1); + strncat(check2, cmd->sock->hostname, strlen(cmd->sock->hostname)); + } + + /* Check invite list if channel is invite-only channel */ + if (channel->mode & SILC_CHANNEL_MODE_INVITE) { + if (!channel->invite_list || + (!silc_string_match(channel->invite_list, check) && + !silc_string_match(channel->invite_list, check2))) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, + SILC_STATUS_ERR_NOT_INVITED); + goto out; + } } - } - /* Get passphrase */ - tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len); - if (tmp) { - passphrase = silc_calloc(tmp_len, sizeof(*passphrase)); - memcpy(passphrase, tmp, tmp_len); + /* Check ban list if it exists. If the client's nickname, server, + username and/or hostname is in the ban list the access to the + channel is denied. */ + if (channel->ban_list) { + if (silc_string_match(channel->ban_list, check) || + silc_string_match(channel->ban_list, check2)) { + silc_server_command_send_status_reply( + cmd, SILC_COMMAND_JOIN, + SILC_STATUS_ERR_BANNED_FROM_CHANNEL); + goto out; + } + } + + /* Check user count limit if set. */ + if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) { + if (silc_hash_table_count(channel->user_list) + 1 > + channel->user_limit) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, + SILC_STATUS_ERR_CHANNEL_IS_FULL); + goto out; + } + } } - + /* Check the channel passphrase if set. */ if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) { + /* Get passphrase */ + tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len); + if (tmp) { + passphrase = silc_calloc(tmp_len, sizeof(*passphrase)); + memcpy(passphrase, tmp, tmp_len); + } + if (!passphrase || !channel->passphrase || - memcmp(channel->passphrase, passphrase, - strlen(channel->passphrase))) { + memcmp(passphrase, channel->passphrase, strlen(channel->passphrase))) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, SILC_STATUS_ERR_BAD_PASSWORD); goto out; } } - /* Check user count limit if set. */ - if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) { - if (silc_hash_table_count(channel->user_list) + 1 > - channel->user_limit) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, - SILC_STATUS_ERR_CHANNEL_IS_FULL); - goto out; - } - } - /* * Client is allowed to join to the channel. Make it happen. */ @@ -3193,7 +3249,7 @@ static void silc_server_command_join_channel(SilcServer server, we'll ignore it (in packet_receive.c) so we must send it here. If we are router then this will send it to local clients and local servers. */ - silc_server_send_notify_to_channel(server, NULL, channel, FALSE, + silc_server_send_notify_to_channel(server, NULL, channel, FALSE, SILC_NOTIFY_TYPE_JOIN, 2, clidp->data, clidp->len, chidp->data, chidp->len); @@ -3211,6 +3267,24 @@ static void silc_server_command_join_channel(SilcServer server, keyp->data, keyp->len, FALSE, TRUE); } + /* If client became founder by providing correct founder auth data + notify the mode change to the channel. */ + if (founder) { + SILC_PUT32_MSB(chl->mode, mode); + silc_server_send_notify_to_channel(server, NULL, channel, FALSE, + SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3, + clidp->data, clidp->len, + mode, 4, clidp->data, clidp->len); + + /* Set CUMODE notify type to network */ + if (!server->standalone) + silc_server_send_notify_cumode(server, server->router->connection, + server->server_type == SILC_ROUTER ? + TRUE : FALSE, channel, + chl->mode, client->id, SILC_ID_CLIENT, + client->id); + } + silc_buffer_free(reply); silc_buffer_free(clidp); silc_buffer_free(chidp); @@ -3229,14 +3303,15 @@ SILC_SERVER_CMD_FUNC(join) { SilcServerCommandContext cmd = (SilcServerCommandContext)context; SilcServer server = cmd->server; - uint32 tmp_len; + unsigned char *auth; + uint32 tmp_len, auth_len; char *tmp, *channel_name = NULL, *cipher, *hmac; SilcChannelEntry channel; uint32 umode = 0; bool created = FALSE, create_key = TRUE; SilcClientID *client_id; - SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_JOIN, cmd, 1, 4); + SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_JOIN, cmd, 2, 6); /* Get channel name */ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len); @@ -3247,10 +3322,10 @@ SILC_SERVER_CMD_FUNC(join) } channel_name = tmp; - if (strlen(channel_name) > 256) + if (tmp_len > 256) channel_name[255] = '\0'; - if (silc_server_command_bad_chars(channel_name) == TRUE) { + if (silc_server_name_bad_chars(channel_name, tmp_len) == TRUE) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, SILC_STATUS_ERR_BAD_CHANNEL); goto out; @@ -3270,25 +3345,18 @@ SILC_SERVER_CMD_FUNC(join) goto out; } - /* Get cipher and hmac name */ + /* Get cipher, hmac name and auth payload */ cipher = silc_argument_get_arg_type(cmd->args, 4, NULL); hmac = silc_argument_get_arg_type(cmd->args, 5, NULL); + auth = silc_argument_get_arg_type(cmd->args, 6, &auth_len); /* See if the channel exists */ channel = silc_idlist_find_channel_by_name(server->local_list, channel_name, NULL); if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) { - /* If this is coming from client the Client ID in the command packet must - be same as the client's ID. */ - if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) { - SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data; - if (!SILC_ID_CLIENT_COMPARE(entry->id, client_id)) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, - SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); - goto out; - } - } + SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data; + client_id = silc_id_dup(entry->id, SILC_ID_CLIENT); if (!channel || channel->disabled) { /* Channel not found */ @@ -3299,8 +3367,9 @@ SILC_SERVER_CMD_FUNC(join) channel = silc_server_create_new_channel(server, server->id, cipher, hmac, channel_name, TRUE); if (!channel) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, - SILC_STATUS_ERR_UNKNOWN_ALGORITHM); + silc_server_command_send_status_reply( + cmd, SILC_COMMAND_JOIN, + SILC_STATUS_ERR_UNKNOWN_ALGORITHM); goto out; } @@ -3337,11 +3406,12 @@ SILC_SERVER_CMD_FUNC(join) /* Reprocess this packet after received reply from router */ silc_server_command_pending(server, SILC_COMMAND_JOIN, silc_command_get_ident(cmd->payload), - silc_server_command_destructor, silc_server_command_join, silc_server_command_dup(cmd)); cmd->pending = TRUE; - return; + silc_command_set_ident(cmd->payload, old_ident); + silc_buffer_free(tmpbuf); + goto out; } /* We are router and the channel does not seem exist so we will check @@ -3400,11 +3470,17 @@ SILC_SERVER_CMD_FUNC(join) if (cmd->pending && context2) { SilcServerCommandReplyContext reply = (SilcServerCommandReplyContext)context2; + if (silc_command_get(reply->payload) == SILC_COMMAND_JOIN) { tmp = silc_argument_get_arg_type(reply->args, 6, NULL); SILC_GET32_MSB(created, tmp); - create_key = FALSE; /* Router returned the key already */ + if (silc_argument_get_arg_type(reply->args, 7, NULL)) + create_key = FALSE; /* Router returned the key already */ } + + if (silc_command_get(reply->payload) == SILC_COMMAND_WHOIS && + !silc_hash_table_count(channel->user_list)) + created = TRUE; } /* If the channel does not have global users and is also empty the client @@ -3414,7 +3490,8 @@ SILC_SERVER_CMD_FUNC(join) /* Join to the channel */ silc_server_command_join_channel(server, cmd, channel, client_id, - created, create_key, umode); + created, create_key, umode, + auth, auth_len); silc_free(client_id); @@ -3449,10 +3526,10 @@ SILC_SERVER_CMD_FUNC(motd) idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER); - if (server->config && server->config->motd && - server->config->motd->motd_file) { + if (server->config && server->config->server_info && + server->config->server_info->motd_file) { /* Send motd */ - motd = silc_file_readfile(server->config->motd->motd_file, &motd_len); + motd = silc_file_readfile(server->config->server_info->motd_file, &motd_len); if (!motd) goto out; @@ -3500,13 +3577,12 @@ SILC_SERVER_CMD_FUNC(motd) /* Reprocess this packet after received reply from router */ silc_server_command_pending(server, SILC_COMMAND_MOTD, silc_command_get_ident(cmd->payload), - silc_server_command_destructor, silc_server_command_motd, silc_server_command_dup(cmd)); cmd->pending = TRUE; silc_command_set_ident(cmd->payload, old_ident); silc_buffer_free(tmpbuf); - return; + goto out; } if (!entry && !cmd->pending && !server->standalone) { @@ -3525,13 +3601,12 @@ SILC_SERVER_CMD_FUNC(motd) /* Reprocess this packet after received reply from router */ silc_server_command_pending(server, SILC_COMMAND_MOTD, silc_command_get_ident(cmd->payload), - silc_server_command_destructor, silc_server_command_motd, silc_server_command_dup(cmd)); cmd->pending = TRUE; silc_command_set_ident(cmd->payload, old_ident); silc_buffer_free(tmpbuf); - return; + goto out; } if (!entry) { @@ -3541,18 +3616,12 @@ SILC_SERVER_CMD_FUNC(motd) } idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER); - - if (entry->motd) - packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD, - SILC_STATUS_OK, ident, 2, - 2, idp, idp->len, - 3, entry->motd, - strlen(entry->motd)); - else - packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD, - SILC_STATUS_OK, ident, 1, - 2, idp, idp->len); - + packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD, + SILC_STATUS_OK, ident, 2, + 2, idp, idp->len, + 3, entry->motd, + entry->motd ? + strlen(entry->motd) : 0); silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, packet->data, packet->len, FALSE); silc_buffer_free(packet); @@ -3603,9 +3672,14 @@ SILC_SERVER_CMD_FUNC(umode) goto out; } } else { - if (client->mode & SILC_UMODE_SERVER_OPERATOR) - /* Remove the server operator rights */ + /* Remove the server operator rights */ + if (client->mode & SILC_UMODE_SERVER_OPERATOR) { client->mode &= ~SILC_UMODE_SERVER_OPERATOR; + if (client->connection) + server->stat.my_server_ops--; + if (server->server_type == SILC_ROUTER) + server->stat.server_ops--; + } } if (mask & SILC_UMODE_ROUTER_OPERATOR) { @@ -3616,9 +3690,14 @@ SILC_SERVER_CMD_FUNC(umode) goto out; } } else { - if (client->mode & SILC_UMODE_ROUTER_OPERATOR) - /* Remove the router operator rights */ + /* Remove the router operator rights */ + if (client->mode & SILC_UMODE_ROUTER_OPERATOR) { client->mode &= ~SILC_UMODE_ROUTER_OPERATOR; + if (client->connection) + server->stat.my_router_ops--; + if (server->server_type == SILC_ROUTER) + server->stat.router_ops--; + } } if (mask & SILC_UMODE_GONE) { @@ -3728,7 +3807,7 @@ SILC_SERVER_CMD_FUNC(cmode) SilcChannelClientEntry chl; SilcBuffer packet, cidp; unsigned char *tmp, *tmp_id, *tmp_mask; - char *cipher = NULL, *hmac = NULL; + char *cipher = NULL, *hmac = NULL, *passphrase = NULL; uint32 mode_mask, tmp_len, tmp_len2; uint16 ident = silc_command_get_ident(cmd->payload); @@ -3801,22 +3880,22 @@ SILC_SERVER_CMD_FUNC(cmode) /* The mode is removed and we need to generate and distribute new channel key. Clients are not using private channel keys anymore after this. */ - + /* Re-generate channel key */ if (!silc_server_create_channel_key(server, channel, 0)) goto out; - + /* Send the channel key. This sends it to our local clients and if we are normal server to our router as well. */ silc_server_send_channel_key(server, NULL, channel, server->server_type == SILC_ROUTER ? FALSE : !server->standalone); - + cipher = channel->channel_key->cipher->name; hmac = (char *)silc_hmac_get_name(channel->hmac); } } - + if (mode_mask & SILC_CHANNEL_MODE_ULIMIT) { /* User limit is set on channel */ uint32 user_limit; @@ -3852,7 +3931,7 @@ SILC_SERVER_CMD_FUNC(cmode) } /* Save the passphrase */ - channel->passphrase = strdup(tmp); + passphrase = channel->passphrase = strdup(tmp); } } else { if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) { @@ -4063,14 +4142,16 @@ SILC_SERVER_CMD_FUNC(cmode) /* Finally, set the mode */ channel->mode = mode_mask; - /* Send CMODE_CHANGE notify */ + /* Send CMODE_CHANGE notify. */ cidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); silc_server_send_notify_to_channel(server, NULL, channel, FALSE, - SILC_NOTIFY_TYPE_CMODE_CHANGE, 4, + SILC_NOTIFY_TYPE_CMODE_CHANGE, 5, cidp->data, cidp->len, tmp_mask, 4, cipher, cipher ? strlen(cipher) : 0, - hmac, hmac ? strlen(hmac) : 0); + hmac, hmac ? strlen(hmac) : 0, + passphrase, passphrase ? + strlen(passphrase) : 0); /* Set CMODE notify type to network */ if (!server->standalone) @@ -4078,7 +4159,7 @@ SILC_SERVER_CMD_FUNC(cmode) server->server_type == SILC_ROUTER ? TRUE : FALSE, channel, mode_mask, client->id, SILC_ID_CLIENT, - cipher, hmac); + cipher, hmac, passphrase); /* Send command reply to sender */ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE, @@ -4090,7 +4171,7 @@ SILC_SERVER_CMD_FUNC(cmode) silc_buffer_free(packet); silc_free(channel_id); - silc_free(cidp); + silc_buffer_free(cidp); out: silc_server_command_free(cmd); @@ -4213,9 +4294,9 @@ SILC_SERVER_CMD_FUNC(cumode) /* If the target client is founder, no one else can change their mode but themselves. */ - if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && chl->client != target_client) { + if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && client != target_client) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE, - SILC_STATUS_ERR_NOT_YOU); + SILC_STATUS_ERR_NO_CHANNEL_PRIV); goto out; } @@ -4311,7 +4392,7 @@ SILC_SERVER_CMD_FUNC(cumode) /* Send notify to channel, notify only if mode was actually changed. */ if (notify) { - silc_server_send_notify_to_channel(server, NULL, channel, FALSE, + silc_server_send_notify_to_channel(server, NULL, channel, FALSE, SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3, idp->data, idp->len, tmp_mask, 4, @@ -4358,8 +4439,8 @@ SILC_SERVER_CMD_FUNC(kick) SilcChannelEntry channel; SilcChannelClientEntry chl; SilcBuffer idp; - uint32 tmp_len; - unsigned char *tmp, *comment; + uint32 tmp_len, target_idp_len; + unsigned char *tmp, *comment, *target_idp; SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 3); @@ -4406,13 +4487,13 @@ SILC_SERVER_CMD_FUNC(kick) } /* Get target Client ID */ - tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); - if (!tmp) { + target_idp = silc_argument_get_arg_type(cmd->args, 2, &target_idp_len); + if (!target_idp) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK, SILC_STATUS_ERR_NO_CLIENT_ID); goto out; } - client_id = silc_id_payload_parse_id(tmp, tmp_len); + client_id = silc_id_payload_parse_id(target_idp, target_idp_len); if (!client_id) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK, SILC_STATUS_ERR_NO_CLIENT_ID); @@ -4454,12 +4535,12 @@ SILC_SERVER_CMD_FUNC(kick) SILC_STATUS_OK); /* Send KICKED notify to local clients on the channel */ - idp = silc_id_payload_encode(target_client->id, SILC_ID_CLIENT); + idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); silc_server_send_notify_to_channel(server, NULL, channel, FALSE, - SILC_NOTIFY_TYPE_KICKED, - comment ? 2 : 1, - idp->data, idp->len, - comment, comment ? strlen(comment) : 0); + SILC_NOTIFY_TYPE_KICKED, 3, + target_idp, target_idp_len, + comment, comment ? strlen(comment) : 0, + idp->data, idp->len); silc_buffer_free(idp); /* Remove the client from the channel. If the channel does not exist @@ -4474,7 +4555,7 @@ SILC_SERVER_CMD_FUNC(kick) silc_server_send_notify_kicked(server, server->router->connection, server->server_type == SILC_ROUTER ? TRUE : FALSE, channel, - target_client->id, comment); + target_client->id, client->id, comment); if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) { /* Re-generate channel key */ @@ -4502,7 +4583,7 @@ SILC_SERVER_CMD_FUNC(oper) SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data; unsigned char *username, *auth; uint32 tmp_len; - SilcServerConfigSectionAdminConnection *admin; + SilcServerConfigSectionAdmin *admin; SilcIDListData idata = (SilcIDListData)client; SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_OPER, cmd, 1, 2); @@ -4551,6 +4632,12 @@ SILC_SERVER_CMD_FUNC(oper) /* Client is now server operator */ client->mode |= SILC_UMODE_SERVER_OPERATOR; + /* Update statistics */ + if (client->connection) + server->stat.my_server_ops++; + if (server->server_type == SILC_ROUTER) + server->stat.server_ops++; + /* Send UMODE change to primary router */ if (!server->standalone) silc_server_send_notify_umode(server, server->router->connection, TRUE, @@ -4574,7 +4661,7 @@ SILC_SERVER_CMD_FUNC(silcoper) SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data; unsigned char *username, *auth; uint32 tmp_len; - SilcServerConfigSectionAdminConnection *admin; + SilcServerConfigSectionAdmin *admin; SilcIDListData idata = (SilcIDListData)client; SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SILCOPER, cmd, 1, 2); @@ -4629,6 +4716,12 @@ SILC_SERVER_CMD_FUNC(silcoper) /* Client is now router operator */ client->mode |= SILC_UMODE_ROUTER_OPERATOR; + /* Update statistics */ + if (client->connection) + server->stat.my_router_ops++; + if (server->server_type == SILC_ROUTER) + server->stat.router_ops++; + /* Send UMODE change to primary router */ if (!server->standalone) silc_server_send_notify_umode(server, server->router->connection, TRUE, @@ -4751,7 +4844,11 @@ SILC_SERVER_CMD_FUNC(ban) } /* Get entry to the channel user list */ - silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl); + if (!silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl)) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN, + SILC_STATUS_ERR_NOT_ON_CHANNEL); + goto out; + } /* The client must be at least channel operator. */ if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) { @@ -4806,19 +4903,13 @@ SILC_SERVER_CMD_FUNC(ban) TRUE : FALSE, channel, add, del); /* Send the reply back to the client */ - if (channel->ban_list) - packet = - silc_command_reply_payload_encode_va(SILC_COMMAND_BAN, - SILC_STATUS_OK, ident, 2, - 2, id, id_len, - 3, channel->ban_list, - strlen(channel->ban_list) - 1); - else - packet = - silc_command_reply_payload_encode_va(SILC_COMMAND_BAN, - SILC_STATUS_OK, ident, 1, - 2, id, id_len); - + packet = + silc_command_reply_payload_encode_va(SILC_COMMAND_BAN, + SILC_STATUS_OK, ident, 2, + 2, id, id_len, + 3, channel->ban_list, + channel->ban_list ? + strlen(channel->ban_list) -1 : 0); silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, packet->data, packet->len, FALSE); @@ -4894,7 +4985,7 @@ SILC_SERVER_CMD_FUNC(close) server->router = NULL; server->standalone = TRUE; } - silc_server_free_sock_user_data(server, sock); + silc_server_free_sock_user_data(server, sock, NULL); silc_server_close_connection(server, sock); out: @@ -5081,15 +5172,13 @@ SILC_SERVER_CMD_FUNC(users) /* Reprocess this packet after received reply */ silc_server_command_pending(server, SILC_COMMAND_USERS, silc_command_get_ident(cmd->payload), - silc_server_command_destructor, silc_server_command_users, silc_server_command_dup(cmd)); cmd->pending = TRUE; silc_command_set_ident(cmd->payload, ident); - silc_buffer_free(tmpbuf); silc_free(id); - return; + goto out; } /* Check the global list as well. */ @@ -5172,6 +5261,7 @@ SILC_SERVER_CMD_FUNC(getkey) uint32 tmp_len, pklen; SilcBuffer pk = NULL; SilcIdType id_type; + SilcPublicKey public_key; SILC_LOG_DEBUG(("Start")); @@ -5181,7 +5271,7 @@ SILC_SERVER_CMD_FUNC(getkey) SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - idp = silc_id_payload_parse_data(tmp, tmp_len); + idp = silc_id_payload_parse(tmp, tmp_len); if (!idp) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY, SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); @@ -5223,14 +5313,12 @@ SILC_SERVER_CMD_FUNC(getkey) /* Reprocess this packet after received reply from router */ silc_server_command_pending(server, SILC_COMMAND_GETKEY, silc_command_get_ident(cmd->payload), - silc_server_command_destructor, silc_server_command_getkey, silc_server_command_dup(cmd)); cmd->pending = TRUE; - silc_command_set_ident(cmd->payload, old_ident); silc_buffer_free(tmpbuf); - return; + goto out; } if (!client) { @@ -5242,11 +5330,12 @@ SILC_SERVER_CMD_FUNC(getkey) /* The client is locally connected, just get the public key and send it back. If they key does not exist then do not send it, send just OK reply */ - if (!client->data.public_key) { + public_key = client->data.public_key; + if (!public_key) { pkdata = NULL; pklen = 0; } else { - tmp = silc_pkcs_public_key_encode(client->data.public_key, &tmp_len); + tmp = silc_pkcs_public_key_encode(public_key, &tmp_len); pk = silc_buffer_alloc(4 + tmp_len); silc_buffer_pull_tail(pk, SILC_BUFFER_END(pk)); silc_buffer_format(pk, @@ -5289,14 +5378,12 @@ SILC_SERVER_CMD_FUNC(getkey) /* Reprocess this packet after received reply from router */ silc_server_command_pending(server, SILC_COMMAND_GETKEY, silc_command_get_ident(cmd->payload), - silc_server_command_destructor, silc_server_command_getkey, silc_server_command_dup(cmd)); cmd->pending = TRUE; - silc_command_set_ident(cmd->payload, old_ident); silc_buffer_free(tmpbuf); - return; + goto out; } if (!server_entry) { @@ -5306,12 +5393,14 @@ SILC_SERVER_CMD_FUNC(getkey) } /* If they key does not exist then do not send it, send just OK reply */ - if (!server_entry->data.public_key) { + public_key = (!server_entry->data.public_key ? + (server_entry == server->id_entry ? server->public_key : + NULL) : server_entry->data.public_key); + if (!public_key) { pkdata = NULL; pklen = 0; } else { - tmp = silc_pkcs_public_key_encode(server_entry->data.public_key, - &tmp_len); + tmp = silc_pkcs_public_key_encode(public_key, &tmp_len); pk = silc_buffer_alloc(4 + tmp_len); silc_buffer_pull_tail(pk, SILC_BUFFER_END(pk)); silc_buffer_format(pk,