SilcNotifyPayload payload;
SilcNotifyType type;
SilcArgumentPayload args;
- SilcChannelID *channel_id;
+ SilcChannelID *channel_id, *channel_id2;
SilcClientID *client_id, *client_id2;
+ SilcServerID *server_id;
SilcChannelEntry channel;
SilcClientEntry client;
+ SilcServerEntry server_entry;
SilcChannelClientEntry chl;
SilcIDCacheEntry cache;
- unsigned int mode;
+ SilcHashTableList htl;
+ uint32 mode;
unsigned char *tmp;
- unsigned int tmp_len;
+ uint32 tmp_len;
SILC_LOG_DEBUG(("Start"));
/* Get the route to the client */
dst_sock = silc_server_get_client_route(server, packet->dst_id,
- packet->dst_id_len, &idata);
+ packet->dst_id_len, NULL, &idata);
if (dst_sock)
/* Relay the packet */
silc_server_relay_packet(server, dst_sock, idata->send_key,
- idata->hmac, packet, TRUE);
+ idata->hmac_receive, packet, TRUE);
}
/* If we are router and this packet is not already broadcast packet
goto out;
client =
- silc_idlist_add_client(server->global_list, NULL, 0, NULL, NULL,
+ silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
silc_id_dup(client_id, SILC_ID_CLIENT),
sock->user_data, NULL);
if (!client) {
chl = silc_calloc(1, sizeof(*chl));
chl->client = client;
chl->channel = channel;
- silc_list_add(channel->user_list, chl);
- silc_list_add(client->channels, chl);
+ silc_hash_table_add(channel->user_list, client, chl);
+ silc_hash_table_add(client->channels, channel, chl);
silc_free(client_id);
break;
if (tmp_len > 128)
tmp = NULL;
- /* Remove the client from all channels */
- silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, TRUE);
+ /* Remove the client from all channels. */
+ silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE);
client->data.registered = FALSE;
cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
-
-#if 0
- /* Remove the client entry */
- if (!silc_idlist_del_client(server->global_list, client))
- silc_idlist_del_client(server->local_list, client);
-#endif
+ server->stat.clients--;
+ if (server->server_type == SILC_ROUTER)
+ server->stat.cell_clients--;
break;
case SILC_NOTIFY_TYPE_TOPIC_SET:
if (channel->topic)
silc_free(channel->topic);
- channel->topic = silc_calloc(tmp_len, sizeof(*channel->topic));
+ channel->topic = silc_calloc(tmp_len + 1, sizeof(*channel->topic));
memcpy(channel->topic, tmp, tmp_len);
/* Send the same notify to the channel */
SILC_GET32_MSB(mode, tmp);
+ /* If the channel had private keys set and the mode was removed then
+ we must re-generate and re-distribute a new channel key */
+ if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY &&
+ !(mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+ /* Re-generate channel key */
+ silc_server_create_channel_key(server, channel, 0);
+
+ /* 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);
+ }
+
/* Change mode */
channel->mode = mode;
silc_free(channel_id);
+
+ /* Get the hmac */
+ tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+ if (tmp) {
+ unsigned char hash[32];
+
+ if (channel->hmac)
+ silc_hmac_free(channel->hmac);
+ if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
+ goto out;
+
+ /* Set the HMAC key out of current channel key. The client must do
+ this locally. */
+ silc_hash_make(channel->hmac->hash, channel->key, channel->key_len / 8,
+ hash);
+ silc_hmac_set_key(channel->hmac, hash,
+ silc_hash_len(channel->hmac->hash));
+ memset(hash, 0, sizeof(hash));
+ }
+
break;
case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
silc_free(client_id);
/* Get entry to the channel user list */
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
+ silc_hash_table_list(channel->user_list, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+ SilcChannelClientEntry chl2 = NULL;
+
+ /* If the mode is channel founder and we already find a client
+ to have that mode on the channel we will enforce the sender
+ to change the channel founder mode away. There can be only one
+ channel founder on the channel. */
+ if (server->server_type == SILC_ROUTER &&
+ mode & SILC_CHANNEL_UMODE_CHANFO &&
+ chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ silc_server_send_notify_cumode(server, sock, FALSE, channel,
+ (mode & (~SILC_CHANNEL_UMODE_CHANFO)),
+ server->id, SILC_ID_SERVER,
+ client->id);
+ silc_free(channel_id);
+
+ /* Change the mode back if we changed it */
+ if (chl2)
+ chl2->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+ goto out;
+ }
+
if (chl->client == client) {
/* Change the mode */
chl->mode = mode;
- break;
+ if (!(mode & SILC_CHANNEL_UMODE_CHANFO))
+ break;
+
+ chl2 = chl;
}
+ }
/* Send the same notify to the channel */
silc_server_packet_send_to_channel(server, sock, channel, packet->type,
break;
case SILC_NOTIFY_TYPE_INVITE:
- SILC_LOG_DEBUG(("INVITE notify (not-impl XXX)"));
+
+ if (packet->dst_id_type == SILC_ID_CLIENT)
+ goto out;
+
+ SILC_LOG_DEBUG(("INVITE notify"));
+
+ /* Get Channel ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!channel_id)
+ goto out;
+
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel_id, NULL);
+ if (!channel) {
+ silc_free(channel_id);
+ goto out;
+ }
+ }
+ silc_free(channel_id);
+
+ /* Get the added invite */
+ tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+ if (tmp) {
+ if (!channel->invite_list)
+ channel->invite_list = silc_calloc(tmp_len + 2,
+ sizeof(*channel->invite_list));
+ else
+ channel->invite_list = silc_realloc(channel->invite_list,
+ sizeof(*channel->invite_list) *
+ (tmp_len +
+ strlen(channel->invite_list) +
+ 2));
+ if (tmp[tmp_len - 1] == ',')
+ tmp[tmp_len - 1] = '\0';
+
+ strncat(channel->invite_list, tmp, tmp_len);
+ strncat(channel->invite_list, ",", 1);
+ }
+
+ /* Get the deleted invite */
+ tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+ if (tmp && channel->invite_list) {
+ char *start, *end, *n;
+
+ if (!strncmp(channel->invite_list, tmp,
+ strlen(channel->invite_list) - 1)) {
+ silc_free(channel->invite_list);
+ channel->invite_list = NULL;
+ } else {
+ start = strstr(channel->invite_list, tmp);
+ if (start && strlen(start) >= tmp_len) {
+ end = start + tmp_len;
+ n = silc_calloc(strlen(channel->invite_list) - tmp_len, sizeof(*n));
+ strncat(n, channel->invite_list, start - channel->invite_list);
+ strncat(n, end + 1, ((channel->invite_list +
+ strlen(channel->invite_list)) - end) - 1);
+ silc_free(channel->invite_list);
+ channel->invite_list = n;
+ }
+ }
+ }
+
break;
case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
- SILC_LOG_DEBUG(("CHANNEL CHANGE notify (not-impl XXX)"));
+ /*
+ * Distribute to the local clients on the channel and change the
+ * channel ID.
+ */
+
+ SILC_LOG_DEBUG(("CHANNEL CHANGE"));
+
+ if (sock->type != SILC_SOCKET_TYPE_ROUTER)
+ break;
+
+ /* Get the old Channel ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!channel_id)
+ goto out;
+
+ /* Get the channel entry */
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel_id, NULL);
+ if (!channel) {
+ silc_free(channel_id);
+ goto out;
+ }
+ }
+
+ /* Send the notify to the channel */
+ silc_server_packet_send_to_channel(server, sock, channel, packet->type,
+ FALSE, packet->buffer->data,
+ packet->buffer->len, FALSE);
+
+ /* Get the new Channel ID */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+ channel_id2 = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!channel_id2)
+ goto out;
+
+ SILC_LOG_DEBUG(("Old Channel ID id(%s)",
+ silc_id_render(channel_id, SILC_ID_CHANNEL)));
+ SILC_LOG_DEBUG(("New Channel ID id(%s)",
+ silc_id_render(channel_id2, SILC_ID_CHANNEL)));
+
+ /* Replace the Channel ID */
+ if (!silc_idlist_replace_channel_id(server->global_list, channel_id,
+ channel_id2))
+ if (!silc_idlist_replace_channel_id(server->local_list, channel_id,
+ channel_id2)) {
+ silc_free(channel_id2);
+ channel_id2 = NULL;
+ }
+
+ if (channel_id2) {
+ SilcBuffer users = NULL, users_modes = NULL;
+
+ /* Re-announce our clients on the channel as the ID has changed now */
+ silc_server_announce_get_channel_users(server, channel, &users,
+ &users_modes);
+ if (users) {
+ silc_buffer_push(users, users->data - users->head);
+ silc_server_packet_send(server, sock,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ users->data, users->len, FALSE);
+ silc_buffer_free(users);
+ }
+ if (users_modes) {
+ silc_buffer_push(users_modes, users_modes->data - users_modes->head);
+ silc_server_packet_send_dest(server, sock,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel->id, SILC_ID_CHANNEL,
+ users_modes->data,
+ users_modes->len, FALSE);
+ silc_buffer_free(users_modes);
+ }
+ }
+
+ silc_free(channel_id);
+
break;
case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
- SILC_LOG_DEBUG(("SERVER SIGNOFF notify (not-impl XXX)"));
+ /*
+ * Remove the server entry and all clients that this server owns.
+ */
+
+ SILC_LOG_DEBUG(("SERVER SIGNOFF notify"));
+
+ /* Get Server ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+ server_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!server_id)
+ goto out;
+
+ /* Get server entry */
+ server_entry = silc_idlist_find_server_by_id(server->global_list,
+ server_id, NULL);
+ if (!server_entry) {
+ server_entry = silc_idlist_find_server_by_id(server->local_list,
+ server_id, NULL);
+ if (!server_entry) {
+ silc_free(server_id);
+ goto out;
+ }
+ }
+ silc_free(server_id);
+
+ /* Free all client entries that this server owns as they will
+ become invalid now as well. */
+ silc_server_remove_clients_by_server(server, server_entry, TRUE);
+
+ /* Remove the server entry */
+ if (!silc_idlist_del_server(server->global_list, server_entry))
+ silc_idlist_del_server(server->local_list, server_entry);
+
+ /* XXX update statistics */
+
break;
case SILC_NOTIFY_TYPE_KICKED:
* Distribute the notify to local clients on channels
*/
unsigned char *id;
- unsigned int id_len;
+ uint32 id_len;
SILC_LOG_DEBUG(("KILLED notify"));
if (!strcmp(channel->ban_list, tmp)) {
silc_free(channel->ban_list);
channel->ban_list = NULL;
- break;
- }
-
- start = strstr(channel->ban_list, tmp);
- if (start && strlen(start) >= tmp_len) {
- end = start + tmp_len;
- n = silc_calloc(strlen(channel->ban_list) - tmp_len, sizeof(*n));
- strncat(n, channel->ban_list, start - channel->ban_list);
- strncat(n, end + 1, ((channel->ban_list + strlen(channel->ban_list)) -
- end) - 1);
- silc_free(channel->ban_list);
- channel->ban_list = n;
+ } else {
+ start = strstr(channel->ban_list, tmp);
+ if (start && strlen(start) >= tmp_len) {
+ end = start + tmp_len;
+ n = silc_calloc(strlen(channel->ban_list) - tmp_len, sizeof(*n));
+ strncat(n, channel->ban_list, start - channel->ban_list);
+ strncat(n, end + 1, ((channel->ban_list +
+ strlen(channel->ban_list)) - end) - 1);
+ silc_free(channel->ban_list);
+ channel->ban_list = n;
+ }
}
}
{
SilcPacketContext *new;
SilcBuffer buffer;
- unsigned short len;
+ uint16 len;
- SILC_LOG_DEBUG(("Processing New Notify List"));
+ SILC_LOG_DEBUG(("Processing Notify List"));
if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
packet->src_id_type != SILC_ID_SERVER)
/* Get the route to the client */
dst_sock = silc_server_get_client_route(server, packet->dst_id,
- packet->dst_id_len, &idata);
+ packet->dst_id_len, NULL, &idata);
if (!dst_sock)
return;
/* Send the private message */
silc_server_send_private_message(server, dst_sock, idata->send_key,
- idata->hmac, packet);
+ idata->hmac_send, packet);
}
/* Received private message key packet.. This packet is never for us. It is to
/* Get the route to the client */
dst_sock = silc_server_get_client_route(server, packet->dst_id,
- packet->dst_id_len, &idata);
+ packet->dst_id_len, NULL, &idata);
if (!dst_sock)
return;
/* Relay the packet */
silc_server_relay_packet(server, dst_sock, idata->send_key,
- idata->hmac, packet, FALSE);
+ idata->hmac_send, packet, FALSE);
}
/* Processes incoming command reply packet. The command reply packet may
if (packet->dst_id_type == SILC_ID_SERVER) {
/* For now this must be for us */
- if (SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
+ if (memcmp(packet->dst_id, server->id_string, packet->dst_id_len)) {
SILC_LOG_ERROR(("Cannot process command reply to unknown server"));
return;
}
idata = (SilcIDListData)client;
/* Encrypt packet */
- silc_packet_encrypt(idata->send_key, idata->hmac, dst_sock->outbuf,
+ silc_packet_encrypt(idata->send_key, idata->hmac_send, dst_sock->outbuf,
buffer->len);
/* Send the packet */
SilcPacketContext *packet)
{
SilcChannelEntry channel = NULL;
- SilcChannelClientEntry chl;
SilcChannelID *id = NULL;
void *sender = NULL;
+ void *sender_entry = NULL;
SILC_LOG_DEBUG(("Processing channel message"));
}
}
- /* See that this client is on the channel. If the message is coming
- from router we won't do the check as the message is from client that
- we don't know about. Also, if the original sender is not client
- (as it can be server as well) we don't do the check. */
+ /* See that this client is on the channel. If the original sender is
+ not client (as it can be server as well) we don't do the check. */
sender = silc_id_str2id(packet->src_id, packet->src_id_len,
packet->src_id_type);
if (!sender)
goto out;
- if (sock->type != SILC_SOCKET_TYPE_ROUTER &&
- packet->src_id_type == SILC_ID_CLIENT) {
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
- if (chl->client && !SILC_ID_CLIENT_COMPARE(chl->client->id, sender))
- break;
- }
- if (chl == SILC_LIST_END) {
+ if (packet->src_id_type == SILC_ID_CLIENT) {
+ sender_entry = silc_idlist_find_client_by_id(server->local_list,
+ sender, NULL);
+ if (!sender_entry)
+ sender_entry = silc_idlist_find_client_by_id(server->global_list,
+ sender, NULL);
+ if (!sender_entry || !silc_server_client_on_channel(sender_entry,
+ channel)) {
SILC_LOG_DEBUG(("Client not on channel"));
goto out;
}
}
- /* If we are router and the packet came from router and private key
- has not been set for the channel then we must encrypt the packet
- as it was decrypted with the session key shared between us and the
- router which sent it. This is so, because cells does not share the
- same channel key */
- if (server->server_type == SILC_ROUTER &&
- sock->type == SILC_SOCKET_TYPE_ROUTER &&
- !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
- SilcBuffer chp;
- unsigned int iv_len, i, data_len;
-
- iv_len = silc_cipher_get_block_len(channel->channel_key);
- if (channel->iv[0] == '\0')
- for (i = 0; i < iv_len; i++) channel->iv[i] =
- silc_rng_get_byte(server->rng);
- else
- silc_hash_make(server->md5hash, channel->iv, iv_len, channel->iv);
-
- /* Encode new payload. This encrypts it also. */
- SILC_GET16_MSB(data_len, packet->buffer->data);
- chp = silc_channel_message_payload_encode(data_len,
- packet->buffer->data + 2,
- iv_len, channel->iv,
- channel->channel_key,
- channel->hmac, server->rng);
- silc_buffer_put(packet->buffer, chp->data, chp->len);
- silc_buffer_free(chp);
- }
-
/* Distribute the packet to our local clients. This will send the
packet for further routing as well, if needed. */
silc_server_packet_relay_to_channel(server, sock, channel, sender,
- packet->src_id_type,
+ packet->src_id_type, sender_entry,
packet->buffer->data,
packet->buffer->len, FALSE);
SilcBuffer buffer = packet->buffer;
SilcChannelEntry channel;
- if (packet->src_id_type != SILC_ID_SERVER)
+ if (packet->src_id_type != SILC_ID_SERVER ||
+ (server->server_type == SILC_ROUTER &&
+ sock->type == SILC_SOCKET_TYPE_ROUTER))
return;
/* Save the channel key */
{
SilcBuffer buffer = packet->buffer;
SilcClientEntry client;
- SilcIDCacheEntry cache;
SilcClientID *client_id;
SilcBuffer reply;
SilcIDListData idata;
char *username = NULL, *realname = NULL, *id_string;
+ uint32 id_len;
int ret;
+ char *hostname, *nickname;
SILC_LOG_DEBUG(("Creating new client"));
client = (SilcClientEntry)sock->user_data;
idata = (SilcIDListData)client;
- /* Fetch the old client cache entry so that we can update it. */
- if (!silc_idcache_find_by_context(server->local_list->clients,
- sock->user_data, &cache)) {
+ /* Remove the old cache entry */
+ if (!silc_idcache_del_by_context(server->local_list->clients, client)) {
SILC_LOG_ERROR(("Lost client's cache entry - bad thing"));
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Unknown client");
return NULL;
}
silc_free(username);
if (realname)
silc_free(realname);
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Incomplete client information");
return NULL;
}
return NULL;
}
+ if (strlen(username) > 128)
+ username[127] = '\0';
+
+ nickname = strdup(username);
+
+ /* Make sanity checks for the hostname of the client. If the hostname
+ is provided in the `username' check that it is the same than the
+ resolved hostname, or if not resolved the hostname that appears in
+ the client's public key. If the hostname is not present then put
+ it from the resolved name or from the public key. */
+ if (strchr(username, '@')) {
+ SilcPublicKeyIdentifier pident;
+ int tlen = strcspn(username, "@");
+ char *phostname = NULL;
+
+ hostname = silc_calloc((strlen(username) - tlen) + 1, sizeof(char));
+ memcpy(hostname, username + tlen + 1, strlen(username) - tlen - 1);
+
+ if (strcmp(sock->hostname, sock->ip) &&
+ strcmp(sock->hostname, hostname)) {
+ silc_free(username);
+ silc_free(hostname);
+ if (realname)
+ silc_free(realname);
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Incomplete client information");
+ return NULL;
+ }
+
+ pident = silc_pkcs_decode_identifier(client->data.public_key->identifier);
+ if (pident) {
+ phostname = strdup(pident->host);
+ silc_pkcs_free_identifier(pident);
+ }
+
+ if (!strcmp(sock->hostname, sock->ip) &&
+ phostname && strcmp(phostname, hostname)) {
+ silc_free(username);
+ silc_free(hostname);
+ if (phostname)
+ silc_free(phostname);
+ if (realname)
+ silc_free(realname);
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Incomplete client information");
+ return NULL;
+ }
+
+ if (phostname)
+ silc_free(phostname);
+ } else {
+ /* The hostname is not present, add it. */
+ char *newusername;
+ /* XXX For now we cannot take the host name from the public key since
+ they are not trusted or we cannot verify them as trusted. Just take
+ what the resolved name or address is. */
+#if 0
+ if (strcmp(sock->hostname, sock->ip)) {
+#endif
+ newusername = silc_calloc(strlen(username) +
+ strlen(sock->hostname) + 2,
+ sizeof(*newusername));
+ strncat(newusername, username, strlen(username));
+ strncat(newusername, "@", 1);
+ strncat(newusername, sock->hostname, strlen(sock->hostname));
+ silc_free(username);
+ username = newusername;
+#if 0
+ } else {
+ SilcPublicKeyIdentifier pident =
+ silc_pkcs_decode_identifier(client->data.public_key->identifier);
+
+ if (pident) {
+ newusername = silc_calloc(strlen(username) +
+ strlen(pident->host) + 2,
+ sizeof(*newusername));
+ strncat(newusername, username, strlen(username));
+ strncat(newusername, "@", 1);
+ strncat(newusername, pident->host, strlen(pident->host));
+ silc_free(username);
+ username = newusername;
+ silc_pkcs_free_identifier(pident);
+ }
+ }
+#endif
+ }
+
/* Create Client ID */
silc_id_create_client_id(server->id, server->rng, server->md5hash,
username, &client_id);
- if (strlen(username) > 128)
- username[127] = '\0';
-
/* Update client entry */
idata->registered = TRUE;
- client->nickname = strdup(username);
+ client->nickname = nickname;
client->username = username;
client->userinfo = realname ? realname : strdup(" ");
client->id = client_id;
+ id_len = silc_id_get_len(client_id, SILC_ID_CLIENT);
- /* Update the cache entry */
- cache->id = (void *)client_id;
- cache->type = SILC_ID_CLIENT;
- cache->data = username;
- silc_idcache_sort_by_data(server->local_list->clients);
+ /* Add the client again to the ID cache */
+ silc_idcache_add(server->local_list->clients, client->nickname,
+ client_id, client, FALSE);
/* Notify our router about new client on the SILC network */
if (!server->standalone)
silc_server_send_new_id(server, (SilcSocketConnection)
server->router->connection,
server->server_type == SILC_ROUTER ? TRUE : FALSE,
- client->id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
+ client->id, SILC_ID_CLIENT, id_len);
/* Send the new client ID to the client. */
id_string = silc_id_id2str(client->id, SILC_ID_CLIENT);
- reply = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_LEN);
+ reply = silc_buffer_alloc(2 + 2 + id_len);
silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply));
silc_buffer_format(reply,
SILC_STR_UI_SHORT(SILC_ID_CLIENT),
- SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
- SILC_STR_UI_XNSTRING(id_string, SILC_ID_CLIENT_LEN),
+ SILC_STR_UI_SHORT(id_len),
+ SILC_STR_UI_XNSTRING(id_string, id_len),
SILC_STR_END);
silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0,
reply->data, reply->len, FALSE);
/* Send some nice info to the client */
SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("Welcome to the SILC Network %s@%s",
- username, sock->hostname));
+ ("Welcome to the SILC Network %s",
+ username));
SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
("Your host is %s, running version %s",
server->config->server_info->server_name,
{
SilcBuffer buffer = packet->buffer;
SilcServerEntry new_server;
- SilcIDCacheEntry cache;
SilcServerID *server_id;
SilcIDListData idata;
unsigned char *server_name, *id_string;
- unsigned short id_len, name_len;
+ uint16 id_len, name_len;
int ret;
SILC_LOG_DEBUG(("Creating new server"));
new_server = (SilcServerEntry)sock->user_data;
idata = (SilcIDListData)new_server;
- /* Fetch the old server cache entry so that we can update it. */
- if (!silc_idcache_find_by_context(server->local_list->servers,
- sock->user_data, &cache)) {
- SILC_LOG_ERROR(("Lost server's cache entry - bad thing"));
- return NULL;
- }
+ /* Remove the old cache entry */
+ silc_idcache_del_by_context(server->local_list->servers, new_server);
/* Parse the incoming packet */
ret = silc_buffer_unformat(buffer,
}
silc_free(id_string);
- /* Update client entry */
+ /* Update server entry */
idata->registered = TRUE;
new_server->server_name = server_name;
new_server->id = server_id;
- /* Update the cache entry */
- cache->id = (void *)server_id;
- cache->type = SILC_ID_SERVER;
- cache->data = server_name;
- silc_idcache_sort_by_data(server->local_list->servers);
+ /* Add again the entry to the ID cache. */
+ silc_idcache_add(server->local_list->servers, server_name, server_id,
+ server, FALSE);
/* Distribute the information about new server in the SILC network
to our router. If we are normal server we won't send anything
server->router->connection != sock)
silc_server_send_new_id(server, server->router->connection,
TRUE, new_server->id, SILC_ID_SERVER,
- SILC_ID_SERVER_LEN);
+ silc_id_get_len(server_id, SILC_ID_SERVER));
if (server->server_type == SILC_ROUTER)
server->stat.cell_servers++;
SilcSocketConnection router_sock;
SilcIDPayload idp;
SilcIdType id_type;
- unsigned char *hash = NULL;
void *id;
SILC_LOG_DEBUG(("Processing new ID"));
else
id_list = server->global_list;
- router_sock = sock;
- router = sock->user_data;
+ /* If the packet is coming from server then use the sender as the
+ origin of the the packet. If it came from router then check the real
+ sender of the packet and use that as the origin. */
+ if (sock->type == SILC_SOCKET_TYPE_SERVER) {
+ router_sock = sock;
+ router = sock->user_data;
+ } else {
+ void *sender_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ packet->src_id_type);
+ router = silc_idlist_find_server_by_id(server->global_list,
+ sender_id, NULL);
+ if (!router)
+ router = silc_idlist_find_server_by_id(server->local_list,
+ sender_id, NULL);
+ silc_free(sender_id);
+ if (!router)
+ goto out;
+ router_sock = sock;
+ }
switch(id_type) {
case SILC_ID_CLIENT:
/* As a router we keep information of all global information in our
global list. Cell wide information however is kept in the local
- list. The client is put to global list and we will take the hash
- value of the Client ID and save it to the ID Cache system for fast
- searching in the future. */
- hash = silc_calloc(sizeof(((SilcClientID *)id)->hash),
- sizeof(unsigned char));
- memcpy(hash, ((SilcClientID *)id)->hash,
- sizeof(((SilcClientID *)id)->hash));
- entry = silc_idlist_add_client(id_list, hash,
- sizeof(((SilcClientID *)id)->hash),
- NULL, NULL, id, router, NULL);
+ list. */
+ entry = silc_idlist_add_client(id_list, NULL, NULL, NULL,
+ id, router, NULL);
entry->nickname = NULL;
entry->data.registered = TRUE;
if (sock->type == SILC_SOCKET_TYPE_SERVER)
server->stat.cell_clients++;
server->stat.clients++;
-
-#if 0
- /* XXX Adding two ID's with same IP number replaces the old entry thus
- gives wrong route. Thus, now disabled until figured out a better way
- to do this or when removed the whole thing. This could be removed
- because entry->router->connection gives always the most optimal route
- for the ID anyway (unless new routes (faster perhaps) are established
- after receiving this ID, this we don't know however). */
- /* Add route cache for this ID */
- silc_server_route_add(silc_server_route_hash(
- ((SilcClientID *)id)->ip.s_addr,
- server->id->port), ((SilcClientID *)id)->ip.s_addr,
- router);
-#endif
}
break;
case SILC_ID_SERVER:
+ /* If the ID is mine, ignore it. */
+ if (SILC_ID_SERVER_COMPARE(id, server->id)) {
+ SILC_LOG_DEBUG(("Ignoring my own ID as new ID"));
+ break;
+ }
+
SILC_LOG_DEBUG(("New server id(%s) from [%s] %s",
silc_id_render(id, SILC_ID_SERVER),
sock->type == SILC_SOCKET_TYPE_SERVER ?
"Server" : "Router", sock->hostname));
-
+
/* As a router we keep information of all global information in our global
list. Cell wide information however is kept in the local list. */
silc_idlist_add_server(id_list, NULL, 0, id, router, router_sock);
if (sock->type == SILC_SOCKET_TYPE_SERVER)
server->stat.cell_servers++;
server->stat.servers++;
-
-#if 0
- /* Add route cache for this ID */
- silc_server_route_add(silc_server_route_hash(
- ((SilcServerID *)id)->ip.s_addr,
- ((SilcServerID *)id)->port),
- ((SilcServerID *)id)->ip.s_addr,
- router);
-#endif
break;
case SILC_ID_CHANNEL:
{
SilcPacketContext *new_id;
SilcBuffer idp;
- unsigned short id_len;
+ uint16 id_len;
SILC_LOG_DEBUG(("Processing New ID List"));
SilcChannelPayload payload;
SilcChannelID *channel_id;
char *channel_name;
- unsigned int name_len;
+ uint32 name_len;
unsigned char *id;
- unsigned int id_len;
+ uint32 id_len;
+ uint32 mode;
SILC_LOG_DEBUG(("Processing New Channel"));
return;
}
+ /* Get the mode and set it to the channel */
+ channel->mode = silc_channel_get_mode(payload);
+
/* Send the new channel key to the server */
chk = silc_channel_key_payload_encode(id_len, id,
strlen(channel->channel_key->
/* The channel exist by that name, check whether the ID's match.
If they don't then we'll force the server to use the ID we have.
We also create a new key for the channel. */
+ SilcBuffer users = NULL, users_modes = NULL;
+
+ if (!channel->id)
+ channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
- if (SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
+ if (!SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
/* They don't match, send CHANNEL_CHANGE notify to the server to
force the ID change. */
SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
silc_server_send_notify_channel_change(server, sock, FALSE,
- channel_id,
- channel->id,
- SILC_ID_CHANNEL_LEN);
+ channel_id, channel->id);
+ }
+
+ /* If the mode is different from what we have then enforce the
+ mode change. */
+ mode = silc_channel_get_mode(payload);
+ if (channel->mode != mode) {
+ SILC_LOG_DEBUG(("Forcing the server to change channel mode"));
+ silc_server_send_notify_cmode(server, sock, FALSE, channel,
+ channel->mode, server->id,
+ SILC_ID_SERVER,
+ channel->cipher, channel->hmac_name);
}
/* Create new key for the channel and send it to the server and
everybody else possibly on the channel. */
- silc_server_create_channel_key(server, channel, 0);
-
- /* Send to the channel */
- silc_server_send_channel_key(server, sock, channel, FALSE);
+ if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+ silc_server_create_channel_key(server, channel, 0);
+
+ /* Send to the channel */
+ silc_server_send_channel_key(server, sock, channel, FALSE);
+ id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+ id_len = SILC_ID_CHANNEL_LEN;
+
+ /* Send to the server */
+ chk = silc_channel_key_payload_encode(id_len, id,
+ strlen(channel->channel_key->
+ cipher->name),
+ channel->channel_key->
+ cipher->name,
+ channel->key_len / 8,
+ channel->key);
+ silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0,
+ chk->data, chk->len, FALSE);
+ silc_buffer_free(chk);
+ silc_free(id);
+ }
- /* Send to the server */
- chk = silc_channel_key_payload_encode(id_len, id,
- strlen(channel->channel_key->
- cipher->name),
- channel->channel_key->cipher->name,
- channel->key_len / 8,
- channel->key);
- silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0,
- chk->data, chk->len, FALSE);
- silc_buffer_free(chk);
silc_free(channel_id);
/* Since the channel is coming from server and we also know about it
then send the JOIN notify to the server so that it see's our
users on the channel "joining" the channel. */
- /* XXX TODO **/
+ silc_server_announce_get_channel_users(server, channel, &users,
+ &users_modes);
+ if (users) {
+ silc_buffer_push(users, users->data - users->head);
+ silc_server_packet_send(server, sock,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ users->data, users->len, FALSE);
+ silc_buffer_free(users);
+ }
+ if (users_modes) {
+ silc_buffer_push(users_modes, users_modes->data - users_modes->head);
+ silc_server_packet_send_dest(server, sock,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel->id, SILC_ID_CHANNEL,
+ users_modes->data,
+ users_modes->len, FALSE);
+ silc_buffer_free(users_modes);
+ }
}
}
+
+ silc_channel_payload_free(payload);
}
/* Received New Channel List packet, list of New Channel List payloads inside
{
SilcPacketContext *new;
SilcBuffer buffer;
- unsigned short len1, len2;
+ uint16 len1, len2;
SILC_LOG_DEBUG(("Processing New Channel List"));
/* Get the route to the client */
dst_sock = silc_server_get_client_route(server, packet->dst_id,
- packet->dst_id_len, &idata);
+ packet->dst_id_len, NULL, &idata);
if (!dst_sock)
return;
/* Relay the packet */
silc_server_relay_packet(server, dst_sock, idata->send_key,
- idata->hmac, packet, FALSE);
+ idata->hmac_send, packet, FALSE);
+}
+
+/* Received connection auth request packet that is used during connection
+ phase to resolve the mandatory authentication method. This packet can
+ actually be received at anytime but usually it is used only during
+ the connection authentication phase. Now, protocol says that this packet
+ can come from client or server, however, we support only this coming
+ from client and expect that server's always knows what authentication
+ method to use. */
+
+void silc_server_connection_auth_request(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcServerConfigSectionClientConnection *client = NULL;
+ uint16 conn_type;
+ int ret;
+ SilcAuthMethod auth_meth;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (packet->src_id_type && packet->src_id_type != SILC_ID_CLIENT)
+ return;
+
+ /* Parse the payload */
+ ret = silc_buffer_unformat(packet->buffer,
+ SILC_STR_UI_SHORT(&conn_type),
+ SILC_STR_UI_SHORT(NULL),
+ SILC_STR_END);
+ if (ret == -1)
+ return;
+
+ if (conn_type != SILC_SOCKET_TYPE_CLIENT)
+ return;
+
+ /* Get the authentication method for the client */
+ auth_meth = SILC_AUTH_NONE;
+ client = silc_server_config_find_client_conn(server->config,
+ sock->ip,
+ sock->port);
+ if (!client)
+ client = silc_server_config_find_client_conn(server->config,
+ sock->hostname,
+ sock->port);
+ if (client)
+ auth_meth = client->auth_meth;
+
+ /* Send it back to the client */
+ silc_server_send_connection_auth_request(server, sock,
+ conn_type,
+ auth_meth);
+}
+
+/* Received REKEY packet. The sender of the packet wants to regenerate
+ its session keys. This starts the REKEY protocol. */
+
+void silc_server_rekey(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcProtocol protocol;
+ SilcServerRekeyInternalContext *proto_ctx;
+ SilcIDListData idata = (SilcIDListData)sock->user_data;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ /* Allocate internal protocol context. This is sent as context
+ to the protocol. */
+ proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+ proto_ctx->server = (void *)server;
+ proto_ctx->sock = sock;
+ proto_ctx->responder = TRUE;
+ proto_ctx->pfs = idata->rekey->pfs;
+
+ /* Perform rekey protocol. Will call the final callback after the
+ protocol is over. */
+ silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY,
+ &protocol, proto_ctx, silc_server_rekey_final);
+ sock->protocol = protocol;
+
+ if (proto_ctx->pfs == FALSE)
+ /* Run the protocol */
+ silc_protocol_execute(protocol, server->timeout_queue, 0, 0);
}