+ /* Relay the packet */
+ silc_server_relay_packet(server, dst_sock, idata->send_key,
+ idata->hmac_send, idata->psn_send++, packet, FALSE);
+}
+
+/* Processes incoming command reply packet. The command reply packet may
+ be destined to one of our clients or it may directly for us. We will
+ call the command reply routine after processing the packet. */
+
+void silc_server_command_reply(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer;
+ SilcClientEntry client = NULL;
+ SilcSocketConnection dst_sock;
+ SilcIDListData idata;
+ SilcClientID *id = NULL;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ /* Source must be server or router */
+ if (packet->src_id_type != SILC_ID_SERVER &&
+ sock->type != SILC_SOCKET_TYPE_ROUTER)
+ return;
+
+ if (packet->dst_id_type == SILC_ID_CHANNEL)
+ return;
+
+ if (packet->dst_id_type == SILC_ID_CLIENT) {
+ /* Destination must be one of ours */
+ id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
+ if (!id)
+ return;
+ client = silc_idlist_find_client_by_id(server->local_list, id, TRUE, NULL);
+ if (!client) {
+ SILC_LOG_ERROR(("Cannot process command reply to unknown client"));
+ silc_free(id);
+ return;
+ }
+ }
+
+ if (packet->dst_id_type == SILC_ID_SERVER) {
+ /* For now this must be for us */
+ if (memcmp(packet->dst_id, server->id_string, server->id_string_len)) {
+ SILC_LOG_ERROR(("Cannot process command reply to unknown server"));
+ return;
+ }
+ }
+
+ /* Execute command reply locally for the command */
+ silc_server_command_reply_process(server, sock, buffer);
+
+ if (packet->dst_id_type == SILC_ID_CLIENT && client && id) {
+ /* Relay the packet to the client */
+
+ dst_sock = (SilcSocketConnection)client->connection;
+ silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len
+ + packet->dst_id_len + packet->padlen);
+
+ silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
+ silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
+
+ idata = (SilcIDListData)client;
+
+ /* Encrypt packet */
+ silc_packet_encrypt(idata->send_key, idata->hmac_send, idata->psn_send++,
+ dst_sock->outbuf, buffer->len);
+
+ /* Send the packet */
+ silc_server_packet_send_real(server, dst_sock, TRUE);
+
+ silc_free(id);
+ }
+}
+
+/* Process received channel message. The message can be originated from
+ client or server. */
+
+void silc_server_channel_message(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcChannelEntry channel = NULL;
+ SilcChannelID *id = NULL;
+ void *sender = NULL;
+ void *sender_entry = NULL;
+ bool local = TRUE;
+
+ SILC_LOG_DEBUG(("Processing channel message"));
+
+ /* Sanity checks */
+ if (packet->dst_id_type != SILC_ID_CHANNEL) {
+ SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
+ goto out;
+ }
+
+ /* Find channel entry */
+ id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL);
+ if (!id)
+ goto out;
+ channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+ if (!channel) {
+ SILC_LOG_DEBUG(("Could not find channel"));
+ goto out;
+ }
+ }
+
+ /* 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 (packet->src_id_type == SILC_ID_CLIENT) {
+ sender_entry = silc_idlist_find_client_by_id(server->local_list,
+ sender, TRUE, NULL);
+ if (!sender_entry) {
+ local = FALSE;
+ sender_entry = silc_idlist_find_client_by_id(server->global_list,
+ sender, TRUE, NULL);
+ }
+ if (!sender_entry || !silc_server_client_on_channel(sender_entry,
+ channel)) {
+ SILC_LOG_DEBUG(("Client not on channel"));
+ goto out;
+ }
+
+ /* If the packet is coming from router, but the client entry is
+ local entry to us then some router is rerouting this to us and it is
+ not allowed. */
+ if (server->server_type == SILC_ROUTER &&
+ sock->type == SILC_SOCKET_TYPE_ROUTER && local) {
+ SILC_LOG_DEBUG(("Channel message rerouted to the sender, drop it"));
+ goto out;
+ }
+ }
+
+ /* 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, sender_entry,
+ packet->buffer->data,
+ packet->buffer->len, FALSE);
+
+ out:
+ if (sender)
+ silc_free(sender);
+ if (id)
+ silc_free(id);
+}
+
+/* Received channel key packet. We distribute the key to all of our locally
+ connected clients on the channel. */
+
+void silc_server_channel_key(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer;
+ SilcChannelEntry channel;
+
+ if (packet->src_id_type != SILC_ID_SERVER ||
+ (server->server_type == SILC_ROUTER &&
+ sock->type == SILC_SOCKET_TYPE_ROUTER))
+ return;
+
+ /* Save the channel key */
+ channel = silc_server_save_channel_key(server, buffer, NULL);
+ if (!channel)
+ return;
+
+ /* Distribute the key to everybody who is on the channel. If we are router
+ we will also send it to locally connected servers. */
+ silc_server_send_channel_key(server, sock, channel, FALSE);
+
+ if (server->server_type != SILC_BACKUP_ROUTER) {
+ /* Distribute to local cell backup routers. */
+ silc_server_backup_send(server, (SilcServerEntry)sock->user_data,
+ SILC_PACKET_CHANNEL_KEY, 0,
+ buffer->data, buffer->len, FALSE, TRUE);
+ }
+}
+
+/* Received New Client packet and processes it. Creates Client ID for the
+ client. Client becomes registered after calling this functions. */
+
+SilcClientEntry silc_server_new_client(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer;
+ SilcClientEntry client;
+ SilcClientID *client_id;
+ SilcBuffer reply;
+ SilcIDListData idata;
+ char *username = NULL, *realname = NULL, *id_string;
+ uint16 username_len;
+ uint32 id_len;
+ int ret;
+ char *hostname, *nickname;
+ int nickfail = 0;
+
+ SILC_LOG_DEBUG(("Creating new client"));
+
+ if (sock->type != SILC_SOCKET_TYPE_CLIENT)
+ return NULL;
+
+ /* Take client entry */
+ client = (SilcClientEntry)sock->user_data;
+ idata = (SilcIDListData)client;
+
+ /* 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;
+ }
+
+ /* Parse incoming packet */
+ ret = silc_buffer_unformat(buffer,
+ SILC_STR_UI16_NSTRING_ALLOC(&username,
+ &username_len),
+ SILC_STR_UI16_STRING_ALLOC(&realname),
+ SILC_STR_END);
+ if (ret == -1) {
+ silc_free(username);
+ silc_free(realname);
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Incomplete client information");
+ return NULL;
+ }
+
+ if (!username) {
+ silc_free(username);
+ silc_free(realname);
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Incomplete client information");
+ return NULL;
+ }
+
+ if (username_len > 128)
+ username[128] = '\0';
+
+ /* Check for bad characters for nickname, and modify the nickname if
+ it includes those. */
+ if (silc_server_name_bad_chars(username, username_len)) {
+ nickname = silc_server_name_modify_bad(username, username_len);
+ } else {
+ 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);
+ 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);
+ silc_free(phostname);
+ silc_free(realname);
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Incomplete client information");
+ return NULL;
+ }
+
+ 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);