+/*
+
+ server.c
+
+ Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ Copyright (C) 1997 - 2000 Pekka Riikonen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+*/
+/*
+ * This is the actual SILC server than handles everything relating to
+ * servicing the SILC connections. This is also a SILC router as a router
+ * is also normal server.
+ */
+/*
+ * $Id$
+ * $Log$
+ * Revision 1.1 2000/06/27 11:36:56 priikone
+ * Initial revision
+ *
+ *
+ */
+
+#include "serverincludes.h"
+#include "server_internal.h"
+
+/* Static prototypes */
+SILC_TASK_CALLBACK(silc_server_connect_to_router);
+SILC_TASK_CALLBACK(silc_server_connect_to_router_second);
+SILC_TASK_CALLBACK(silc_server_connect_to_router_final);
+SILC_TASK_CALLBACK(silc_server_accept_new_connection);
+SILC_TASK_CALLBACK(silc_server_accept_new_connection_second);
+SILC_TASK_CALLBACK(silc_server_accept_new_connection_final);
+SILC_TASK_CALLBACK(silc_server_packet_process);
+SILC_TASK_CALLBACK(silc_server_packet_parse);
+SILC_TASK_CALLBACK(silc_server_timeout_remote);
+
+/* XXX */
+void silc_server_packet_parse_type(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet);
+
+static int silc_server_packet_check_mac(SilcServer server,
+ SilcSocketConnection sock,
+ SilcBuffer buffer);
+static int silc_server_packet_decrypt_rest(SilcServer server,
+ SilcSocketConnection sock,
+ SilcBuffer buffer);
+static int silc_server_packet_decrypt_rest_special(SilcServer server,
+ SilcSocketConnection sock,
+ SilcBuffer buffer);
+
+extern char server_version[];
+
+/* Allocates a new SILC server object. This has to be done before the server
+ can be used. After allocation one must call silc_server_init to initialize
+ the server. The new allocated server object is returned to the new_server
+ argument. */
+
+int silc_server_alloc(SilcServer *new_server)
+{
+ SILC_LOG_DEBUG(("Allocating new server object"));
+
+ *new_server = silc_calloc(1, sizeof(**new_server));
+ if (*new_server == NULL) {
+ SILC_LOG_ERROR(("Could not allocate new server object"));
+ return FALSE;
+ }
+
+ /* Set default values */
+ (*new_server)->server_name = NULL;
+ (*new_server)->server_type = SILC_SERVER;
+ (*new_server)->standalone = FALSE;
+ (*new_server)->id = NULL;
+ (*new_server)->io_queue = NULL;
+ (*new_server)->timeout_queue = NULL;
+ (*new_server)->local_list = silc_calloc(1, sizeof(SilcIDListObject));
+ (*new_server)->global_list = silc_calloc(1, sizeof(SilcIDListObject));
+ (*new_server)->rng = NULL;
+ (*new_server)->md5hash = NULL;
+ (*new_server)->sha1hash = NULL;
+ /* (*new_server)->public_key = NULL;*/
+
+ return TRUE;
+}
+
+/* Free's the SILC server object. This is called at the very end before
+ the program ends. */
+
+void silc_server_free(SilcServer server)
+{
+ if (server) {
+ if (server->local_list)
+ silc_free(server->local_list);
+ if (server->global_list)
+ silc_free(server->global_list);
+ if (server->rng)
+ silc_rng_free(server->rng);
+
+ silc_math_primegen_uninit(); /* XXX */
+ silc_free(server);
+ }
+}
+
+/* Initializes the entire SILC server. This is called always before running
+ the server. This is called only once at the initialization of the program.
+ This binds the server to its listenning port. After this function returns
+ one should call silc_server_run to start the server. This returns TRUE
+ when everything is ok to run the server. Configuration file must be
+ read and parsed before calling this. */
+
+int silc_server_init(SilcServer server)
+{
+ int *sock = NULL, sock_count, i;
+ SilcServerID *id;
+ SilcServerList *id_entry;
+ SilcHashObject hash;
+
+ SILC_LOG_DEBUG(("Initializing server"));
+ assert(server);
+ assert(server->config);
+
+ /* Set log files where log message should be saved. */
+ server->config->server = server;
+ silc_config_server_setlogfiles(server->config);
+
+ /* Register all configured ciphers, PKCS and hash functions. */
+ silc_config_server_register_ciphers(server->config);
+ silc_config_server_register_pkcs(server->config);
+ silc_config_server_register_hashfuncs(server->config);
+
+ /* Initialize random number generator for the server. */
+ server->rng = silc_rng_alloc();
+ silc_rng_init(server->rng);
+ silc_math_primegen_init(); /* XXX */
+
+ /* Initialize hash functions for server to use */
+ silc_hash_alloc("md5", &server->md5hash);
+ silc_hash_alloc("sha1", &server->sha1hash);
+
+ /* Initialize none cipher */
+ silc_cipher_alloc("none", &server->none_cipher);
+
+ /* XXXXX Generate RSA key pair */
+ {
+ unsigned char *public_key;
+ unsigned char *private_key;
+ unsigned int pk_len, prv_len;
+
+ if (silc_pkcs_alloc("rsa", &server->public_key) == FALSE) {
+ SILC_LOG_ERROR(("Could not create RSA key pair"));
+ goto err0;
+ }
+
+ if (server->public_key->pkcs->init(server->public_key->context,
+ 1024, server->rng) == FALSE) {
+ SILC_LOG_ERROR(("Could not generate RSA key pair"));
+ goto err0;
+ }
+
+ public_key =
+ server->public_key->pkcs->get_public_key(server->public_key->context,
+ &pk_len);
+ private_key =
+ server->public_key->pkcs->get_private_key(server->public_key->context,
+ &prv_len);
+
+ SILC_LOG_HEXDUMP(("public key"), public_key, pk_len);
+ SILC_LOG_HEXDUMP(("private key"), private_key, prv_len);
+
+ /* XXX Save keys */
+ silc_pkcs_save_public_key(server->public_key, "pubkey.pub",
+ public_key, pk_len);
+
+ memset(public_key, 0, pk_len);
+ memset(private_key, 0, prv_len);
+ silc_free(public_key);
+ silc_free(private_key);
+ }
+
+ /* Create a listening server. Note that our server can listen on
+ multiple ports. All listeners are created here and now. */
+ /* XXX Still check this whether to use server_info or listen_port. */
+ sock_count = 0;
+ while(server->config->listen_port) {
+ int tmp;
+
+ tmp = silc_net_create_server(server->config->listen_port->port,
+ server->config->listen_port->host);
+ if (tmp < 0)
+ goto err0;
+
+ sock = silc_realloc(sock, (sizeof(int *) * (sock_count + 1)));
+ sock[sock_count] = tmp;
+ server->config->listen_port = server->config->listen_port->next;
+ sock_count++;
+ }
+
+ /* Allocate the entire socket list that is used in server. Eventually
+ all connections will have entry in this table (it is a table of
+ pointers to the actual object that is allocated individually
+ later). */
+ server->sockets = silc_calloc(SILC_SERVER_MAX_CONNECTIONS,
+ sizeof(*server->sockets));
+
+ for (i = 0; i < sock_count; i++) {
+ SilcSocketConnection newsocket = NULL;
+
+ /* Set socket to non-blocking mode */
+ silc_net_set_socket_nonblock(sock[i]);
+ server->sock = sock[i];
+
+ /* Create a Server ID for the server. */
+ silc_id_create_server_id(sock[i], server->rng, &id);
+ if (!id) {
+ goto err0;
+ }
+
+ server->id = id;
+ server->id_type = SILC_ID_SERVER;
+ server->server_name = server->config->server_info->server_name;
+
+ /* Add ourselves to the server list. We don't have a router yet
+ beacuse we haven't established a route yet. It will be done later.
+ For now, NULL is sent as router. This allocates new entry to
+ the ID list. */
+ silc_idlist_add_server(&server->local_list->servers,
+ server->config->server_info->server_name,
+ server->server_type, server->id, NULL,
+ server->send_key, server->receive_key,
+ NULL, NULL, &id_entry);
+ if (!id_entry)
+ goto err0;
+
+ /* Add ourselves also to the socket table. The entry allocated above
+ is sent as argument for fast referencing in the future. */
+ silc_socket_alloc(sock[i], SILC_SOCKET_TYPE_SERVER, id_entry,
+ &newsocket);
+ if (!newsocket)
+ goto err0;
+
+ server->sockets[sock[i]] = newsocket;
+
+ /* Put the allocated socket pointer also to the entry allocated above
+ for fast back-referencing to the socket list. */
+ id_entry->connection = (void *)server->sockets[sock[i]];
+ server->id_entry = id_entry;
+ }
+
+ /* Register the task queues. In SILC we have by default three task queues.
+ One task queue for non-timeout tasks which perform different kind of
+ I/O on file descriptors, timeout task queue for timeout tasks, and,
+ generic non-timeout task queue whose tasks apply to all connections. */
+ silc_task_queue_alloc(&server->io_queue, TRUE);
+ if (!server->io_queue) {
+ goto err0;
+ }
+ silc_task_queue_alloc(&server->timeout_queue, TRUE);
+ if (!server->timeout_queue) {
+ goto err1;
+ }
+ silc_task_queue_alloc(&server->generic_queue, TRUE);
+ if (!server->generic_queue) {
+ goto err1;
+ }
+
+ /* Initialize the scheduler */
+ silc_schedule_init(server->io_queue, server->timeout_queue,
+ server->generic_queue,
+ SILC_SERVER_MAX_CONNECTIONS);
+
+ /* Add the first task to the queue. This is task that is executed by
+ timeout. It expires as soon as the caller calls silc_server_run. This
+ task performs authentication protocol and key exchange with our
+ primary router. */
+ if (silc_task_register(server->timeout_queue, sock[0],
+ silc_server_connect_to_router,
+ (void *)server, 0, 1,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL) == NULL) {
+ goto err2;
+ }
+
+ /* If server connections has been configured then we must be router as
+ normal server cannot have server connections, only router connections. */
+ if (server->config->servers)
+ server->server_type = SILC_ROUTER;
+
+ SILC_LOG_DEBUG(("Server initialized"));
+
+ /* We are done here, return succesfully */
+ return TRUE;
+
+ err2:
+ silc_task_queue_free(server->timeout_queue);
+ err1:
+ silc_task_queue_free(server->io_queue);
+ err0:
+ for (i = 0; i < sock_count; i++)
+ silc_net_close_server(sock[i]);
+
+ return FALSE;
+}
+
+/* Stops the SILC server. This function is used to shutdown the server.
+ This is usually called after the scheduler has returned. After stopping
+ the server one should call silc_server_free. */
+
+void silc_server_stop(SilcServer server)
+{
+ SILC_LOG_DEBUG(("Stopping server"));
+
+ /* Stop the scheduler, although it might be already stopped. This
+ doesn't hurt anyone. This removes all the tasks and task queues,
+ as well. */
+ silc_schedule_stop();
+ silc_schedule_uninit();
+
+ SILC_LOG_DEBUG(("Server stopped"));
+}
+
+/* The heart of the server. This runs the scheduler thus runs the server. */
+
+void silc_server_run(SilcServer server)
+{
+ SILC_LOG_DEBUG(("Running server"));
+
+ /* Start the scheduler, the heart of the SILC server. When this returns
+ the program will be terminated. */
+ silc_schedule();
+}
+
+/* This function connects to our primary router or if we are a router this
+ establishes all our primary routes. This is called at the start of the
+ server to do authentication and key exchange with our router - called
+ from schedule. */
+
+SILC_TASK_CALLBACK(silc_server_connect_to_router)
+{
+ SilcServer server = (SilcServer)context;
+ SilcSocketConnection newsocket;
+ int sock;
+
+ SILC_LOG_DEBUG(("Connecting to router(s)"));
+
+ /* if we are normal SILC server we need to connect to our cell's
+ router. */
+ if (server->server_type == SILC_SERVER) {
+ SilcProtocol protocol;
+ SilcServerKEInternalContext *proto_ctx;
+
+ /* Create connection to the router, if configured. */
+ if (server->config->routers) {
+ sock = silc_net_create_connection(server->config->routers->port,
+ server->config->routers->host);
+ if (sock < 0) {
+ SILC_LOG_ERROR(("Could not connect to router"));
+ silc_schedule_stop();
+ return;
+ }
+
+ /* Set socket options */
+ silc_net_set_socket_nonblock(sock);
+ silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+
+ /* Create socket connection for the connection. Even though we
+ know that we are connecting to a router we will mark the socket
+ to be unknown connection until we have executed authentication
+ protocol. */
+ silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
+ server->sockets[sock] = newsocket;
+ newsocket->hostname = server->config->routers->host;
+ newsocket->port = server->config->routers->port;
+
+ /* Allocate internal protocol context. This is sent as context
+ to the protocol. */
+ proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+ proto_ctx->server = context;
+ proto_ctx->sock = newsocket;
+ proto_ctx->rng = server->rng;
+ proto_ctx->responder = FALSE;
+
+ /* Perform key exchange protocol. silc_server_connect_to_router_second
+ will be called after the protocol is finished. */
+ silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE,
+ &protocol, proto_ctx,
+ silc_server_connect_to_router_second);
+ newsocket->protocol = protocol;
+
+ /* Register a timeout task that will be executed if the protocol
+ is not executed within 15 seconds. For now, this is a hard coded
+ limit. After 15 secs the connection will be closed if the key
+ exchange protocol has not been executed. */
+ proto_ctx->timeout_task =
+ silc_task_register(server->timeout_queue, sock,
+ silc_server_timeout_remote,
+ context, 15, 0,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_LOW);
+
+ /* Register the connection for network input and output. This sets
+ that scheduler will listen for incoming packets for this connection
+ and sets that outgoing packets may be sent to this connection as
+ well. However, this doesn't set the scheduler for outgoing traffic,
+ it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
+ later when outgoing data is available. */
+ SILC_REGISTER_CONNECTION_FOR_IO(sock);
+
+ /* Run the protocol */
+ protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0);
+ return;
+ }
+ }
+
+ /* if we are a SILC router we need to establish all of our primary
+ routes. */
+ if (server->server_type == SILC_ROUTER) {
+ SilcConfigServerSectionServerConnection *ptr;
+
+ /* Create the connections to all our routes */
+ ptr = server->config->routers;
+ while (ptr) {
+ SilcProtocol protocol;
+ SilcServerKEInternalContext *proto_ctx;
+
+ /* Create the connection to the remote end */
+ sock = silc_net_create_connection(ptr->port, ptr->host);
+ if (sock < 0) {
+ SILC_LOG_ERROR(("Could not connect to router"));
+ silc_schedule_stop();
+ return;
+ }
+
+ /* Set socket options */
+ silc_net_set_socket_nonblock(sock);
+ silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+
+ /* Create socket connection for the connection. Even though we
+ know that we are connecting to a router we will mark the socket
+ to be unknown connection until we have executed authentication
+ protocol. */
+ silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
+ server->sockets[sock] = newsocket;
+ newsocket->hostname = ptr->host;
+ newsocket->port = ptr->port;
+
+ /* Allocate internal protocol context. This is sent as context
+ to the protocol. */
+ proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+ proto_ctx->server = context;
+ proto_ctx->sock = newsocket;
+ proto_ctx->rng = server->rng;
+ proto_ctx->responder = FALSE;
+
+ /* Perform key exchange protocol. silc_server_connect_to_router_final
+ will be called after the protocol is finished. */
+ silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE,
+ &protocol, proto_ctx,
+ silc_server_connect_to_router_second);
+ newsocket->protocol = protocol;
+
+ /* Register a timeout task that will be executed if the protocol
+ is not executed within 15 seconds. For now, this is a hard coded
+ limit. After 15 secs the connection will be closed if the key
+ exchange protocol has not been executed. */
+ proto_ctx->timeout_task =
+ silc_task_register(server->timeout_queue, sock,
+ silc_server_timeout_remote,
+ context, 15, 0,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_LOW);
+
+ /* Register the connection for network input and output. This sets
+ that scheduler will listen for incoming packets for this connection
+ and sets that outgoing packets may be sent to this connection as
+ well. However, this doesn't set the scheduler for outgoing traffic,
+ it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
+ later when outgoing data is available. */
+ SILC_REGISTER_CONNECTION_FOR_IO(sock);
+
+ /* Run the protocol */
+ protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0);
+
+ if (!ptr->next)
+ return;
+
+ ptr = ptr->next;
+ }
+ }
+
+ SILC_LOG_DEBUG(("No router(s), server will be standalone"));
+
+ /* There wasn't a configured router, we will continue but we don't
+ have a connection to outside world. We will be standalone server. */
+ server->standalone = TRUE;
+
+ /* Add a task to the queue. This task receives new connections to the
+ server. This task remains on the queue until the end of the program. */
+ if (silc_task_register(server->io_queue, fd,
+ silc_server_accept_new_connection,
+ (void *)server, 0, 0,
+ SILC_TASK_FD,
+ SILC_TASK_PRI_NORMAL) == NULL) {
+ silc_schedule_stop();
+ return;
+ }
+}
+
+/* Second part of connecting to router(s). Key exchange protocol has been
+ executed and now we will execute authentication protocol. */
+
+SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
+{
+ SilcProtocol protocol = (SilcProtocol)context;
+ SilcServerKEInternalContext *ctx =
+ (SilcServerKEInternalContext *)protocol->context;
+ SilcServer server = (SilcServer)ctx->server;
+ SilcSocketConnection sock = NULL;
+ SilcServerConnAuthInternalContext *proto_ctx;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+ /* Error occured during protocol */
+ silc_protocol_free(protocol);
+ if (ctx->packet)
+ silc_buffer_free(ctx->packet);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ if (ctx->dest_id)
+ silc_free(ctx->dest_id);
+ silc_free(ctx);
+ sock->protocol = NULL;
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Key exchange failed");
+ return;
+ }
+
+ /* Allocate internal context for the authentication protocol. This
+ is sent as context for the protocol. */
+ proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+ proto_ctx->server = (void *)server;
+ proto_ctx->sock = sock = server->sockets[fd];
+ proto_ctx->ske = ctx->ske; /* Save SKE object from previous protocol */
+ proto_ctx->dest_id_type = ctx->dest_id_type;
+ proto_ctx->dest_id = ctx->dest_id;
+
+ /* Resolve the authentication method used in this connection */
+ proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
+ if (server->config->routers) {
+ SilcConfigServerSectionServerConnection *conn = NULL;
+
+ /* Check if we find a match from user configured connections */
+ conn = silc_config_server_find_router_conn(server->config,
+ sock->hostname,
+ sock->port);
+ if (conn) {
+ /* Match found. Use the configured authentication method */
+ proto_ctx->auth_meth = conn->auth_meth;
+ if (conn->auth_data) {
+ proto_ctx->auth_data = strdup(conn->auth_data);
+ proto_ctx->auth_data_len = strlen(conn->auth_data);
+ }
+ } else {
+ /* No match found. */
+ /* XXX */
+ }
+ } else {
+ /* XXX */
+ }
+
+ /* Free old protocol as it is finished now */
+ silc_protocol_free(protocol);
+ if (ctx->packet)
+ silc_buffer_free(ctx->packet);
+ silc_free(ctx);
+ sock->protocol = NULL;
+
+ /* Allocate the authentication protocol. This is allocated here
+ but we won't start it yet. We will be receiving party of this
+ protocol thus we will wait that connecting party will make
+ their first move. */
+ silc_protocol_alloc(SILC_PROTOCOL_SERVER_CONNECTION_AUTH,
+ &sock->protocol, proto_ctx,
+ silc_server_connect_to_router_final);
+
+ /* Register timeout task. If the protocol is not executed inside
+ this timelimit the connection will be terminated. Currently
+ this is 15 seconds and is hard coded limit (XXX). */
+ proto_ctx->timeout_task =
+ silc_task_register(server->timeout_queue, sock->sock,
+ silc_server_timeout_remote,
+ (void *)server, 15, 0,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_LOW);
+
+ /* Run the protocol */
+ sock->protocol->execute(server->timeout_queue, 0,
+ sock->protocol, sock->sock, 0, 0);
+}
+
+/* Finalizes the connection to router. Registers a server task to the
+ queue so that we can accept new connections. */
+
+SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
+{
+ SilcProtocol protocol = (SilcProtocol)context;
+ SilcServerConnAuthInternalContext *ctx =
+ (SilcServerConnAuthInternalContext *)protocol->context;
+ SilcServer server = (SilcServer)ctx->server;
+ SilcSocketConnection sock = ctx->sock;
+ SilcServerList *id_entry;
+ SilcIDListUnknown *conn_data;
+ SilcBuffer packet;
+ unsigned char *id_string;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+ /* Error occured during protocol */
+ silc_protocol_free(protocol);
+ if (ctx->packet)
+ silc_buffer_free(ctx->packet);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ if (ctx->dest_id)
+ silc_free(ctx->dest_id);
+ silc_free(ctx);
+ sock->protocol = NULL;
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Authentication failed");
+ return;
+ }
+
+ /* Add a task to the queue. This task receives new connections to the
+ server. This task remains on the queue until the end of the program. */
+ if (!server->listenning) {
+ if (silc_task_register(server->io_queue, server->sock,
+ silc_server_accept_new_connection,
+ (void *)server, 0, 0,
+ SILC_TASK_FD,
+ SILC_TASK_PRI_NORMAL) == NULL) {
+ silc_schedule_stop();
+ return;
+ } else {
+ server->listenning = TRUE;
+ }
+ }
+
+ /* Send NEW_SERVER packet to the router. We will become registered
+ to the SILC network after sending this packet. */
+ id_string = silc_id_id2str(server->id, SILC_ID_SERVER);
+ packet = silc_buffer_alloc(2 + 2 + SILC_ID_SERVER_LEN +
+ strlen(server->server_name));
+ silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+ silc_buffer_format(packet,
+ SILC_STR_UI_SHORT(SILC_ID_SERVER_LEN),
+ SILC_STR_UI_XNSTRING(id_string, SILC_ID_SERVER_LEN),
+ SILC_STR_UI_SHORT(strlen(server->server_name)),
+ SILC_STR_UI_XNSTRING(server->server_name,
+ strlen(server->server_name)),
+ SILC_STR_END);
+
+ /* Send the packet */
+ silc_server_packet_send(server, ctx->sock, SILC_PACKET_NEW_SERVER, 0,
+ packet->data, packet->len, TRUE);
+ silc_buffer_free(packet);
+ silc_free(id_string);
+
+ SILC_LOG_DEBUG(("Connected to router %s", sock->hostname));
+
+ /* Add the connected router to local server list */
+ server->standalone = FALSE;
+ conn_data = (SilcIDListUnknown *)sock->user_data;
+ silc_idlist_add_server(&server->local_list->servers,
+ sock->hostname ? sock->hostname : sock->ip,
+ SILC_ROUTER, ctx->dest_id, NULL,
+ conn_data->send_key, conn_data->receive_key,
+ conn_data->pkcs, conn_data->hmac, &id_entry);
+
+ id_entry->hmac_key = conn_data->hmac_key;
+ id_entry->hmac_key_len = conn_data->hmac_key_len;
+ id_entry->connection = sock;
+ sock->user_data = (void *)id_entry;
+ sock->type = SILC_SOCKET_TYPE_ROUTER;
+ server->id_entry->router = id_entry;
+
+ /* Free the temporary connection data context from key exchange */
+ silc_free(conn_data);
+
+ /* Free the protocol object */
+ silc_protocol_free(protocol);
+ if (ctx->packet)
+ silc_buffer_free(ctx->packet);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ silc_free(ctx);
+ sock->protocol = NULL;
+}
+
+/* Accepts new connections to the server. Accepting new connections are
+ done in three parts to make it async. */
+
+SILC_TASK_CALLBACK(silc_server_accept_new_connection)
+{
+ SilcServer server = (SilcServer)context;
+ SilcSocketConnection newsocket;
+ SilcServerKEInternalContext *proto_ctx;
+ int sock;
+
+ SILC_LOG_DEBUG(("Accepting new connection"));
+
+ sock = silc_net_accept_connection(server->sock);
+ if (sock < 0) {
+ SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
+ return;
+ }
+
+ /* Check max connections */
+ if (sock > SILC_SERVER_MAX_CONNECTIONS) {
+ if (server->config->redirect) {
+ /* XXX Redirecting connection to somewhere else now?? */
+ /*silc_server_send_notify("Server is full, trying to redirect..."); */
+ } else {
+ SILC_LOG_ERROR(("Refusing connection, server is full"));
+ }
+ return;
+ }
+
+ /* Set socket options */
+ silc_net_set_socket_nonblock(sock);
+ silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+
+ /* We don't create a ID yet, since we don't know what type of connection
+ this is yet. But, we do add the connection to the socket table. */
+ silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
+ server->sockets[sock] = newsocket;
+
+ /* XXX This MUST be done async as this will block the entire server
+ process. Either we have to do our own resolver stuff or in the future
+ we can use threads. */
+ /* Perform mandatory name and address lookups for the remote host. */
+ silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
+ if (!newsocket->ip || !newsocket->hostname) {
+ SILC_LOG_DEBUG(("IP lookup/DNS lookup failed"));
+ SILC_LOG_ERROR(("IP lookup/DNS lookup failed"));
+ return;
+ }
+
+ /* Allocate internal context for key exchange protocol. This is
+ sent as context for the protocol. */
+ proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+ proto_ctx->server = context;
+ proto_ctx->sock = newsocket;
+ proto_ctx->rng = server->rng;
+ proto_ctx->responder = TRUE;
+
+ /* Prepare the connection for key exchange protocol. We allocate the
+ protocol but will not start it yet. The connector will be the
+ initiator of the protocol thus we will wait for initiation from
+ there before we start the protocol. */
+ silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE,
+ &newsocket->protocol, proto_ctx,
+ silc_server_accept_new_connection_second);
+
+ /* Register a timeout task that will be executed if the connector
+ will not start the key exchange protocol within 15 seconds. For
+ now, this is a hard coded limit. After 15 secs the connection will
+ be closed if the key exchange protocol has not been started. */
+ proto_ctx->timeout_task =
+ silc_task_register(server->timeout_queue, newsocket->sock,
+ silc_server_timeout_remote,
+ context, 15, 0,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_LOW);
+
+ /* Register the connection for network input and output. This sets
+ that scheduler will listen for incoming packets for this connection
+ and sets that outgoing packets may be sent to this connection as well.
+ However, this doesn't set the scheduler for outgoing traffic, it
+ will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
+ later when outgoing data is available. */
+ SILC_REGISTER_CONNECTION_FOR_IO(sock);
+}
+
+/* Second part of accepting new connection. Key exchange protocol has been
+ performed and now it is time to do little connection authentication
+ protocol to figure out whether this connection is client or server
+ and whether it has right to access this server (especially server
+ connections needs to be authenticated). */
+
+SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
+{
+ SilcProtocol protocol = (SilcProtocol)context;
+ SilcServerKEInternalContext *ctx =
+ (SilcServerKEInternalContext *)protocol->context;
+ SilcServer server = (SilcServer)ctx->server;
+ SilcSocketConnection sock = NULL;
+ SilcServerConnAuthInternalContext *proto_ctx;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+ /* Error occured during protocol */
+ silc_protocol_free(protocol);
+ if (ctx->packet)
+ silc_buffer_free(ctx->packet);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ if (ctx->dest_id)
+ silc_free(ctx->dest_id);
+ silc_free(ctx);
+ sock->protocol = NULL;
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Key exchange failed");
+ return;
+ }
+
+ /* Allocate internal context for the authentication protocol. This
+ is sent as context for the protocol. */
+ proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+ proto_ctx->server = (void *)server;
+ proto_ctx->sock = sock = server->sockets[fd];
+ proto_ctx->ske = ctx->ske; /* Save SKE object from previous protocol */
+ proto_ctx->responder = TRUE;
+ proto_ctx->dest_id_type = ctx->dest_id_type;
+ proto_ctx->dest_id = ctx->dest_id;
+
+ /* Free old protocol as it is finished now */
+ silc_protocol_free(protocol);
+ if (ctx->packet)
+ silc_buffer_free(ctx->packet);
+ silc_free(ctx);
+ sock->protocol = NULL;
+
+ /* Allocate the authentication protocol. This is allocated here
+ but we won't start it yet. We will be receiving party of this
+ protocol thus we will wait that connecting party will make
+ their first move. */
+ silc_protocol_alloc(SILC_PROTOCOL_SERVER_CONNECTION_AUTH,
+ &sock->protocol, proto_ctx,
+ silc_server_accept_new_connection_final);
+
+ /* Register timeout task. If the protocol is not executed inside
+ this timelimit the connection will be terminated. Currently
+ this is 60 seconds and is hard coded limit (XXX). */
+ proto_ctx->timeout_task =
+ silc_task_register(server->timeout_queue, sock->sock,
+ silc_server_timeout_remote,
+ (void *)server, 60, 0,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_LOW);
+}
+
+/* Final part of accepting new connection. The connection has now
+ been authenticated and keys has been exchanged. We also know whether
+ this is client or server connection. */
+
+SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
+{
+ SilcProtocol protocol = (SilcProtocol)context;
+ SilcServerConnAuthInternalContext *ctx =
+ (SilcServerConnAuthInternalContext *)protocol->context;
+ SilcServer server = (SilcServer)ctx->server;
+ SilcSocketConnection sock = ctx->sock;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+ /* Error occured during protocol */
+ silc_protocol_free(protocol);
+ if (ctx->packet)
+ silc_buffer_free(ctx->packet);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ if (ctx->dest_id)
+ silc_free(ctx->dest_id);
+ silc_free(ctx);
+ sock->protocol = NULL;
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Authentication failed");
+ return;
+ }
+
+ sock->type = ctx->conn_type;
+ switch(sock->type) {
+ case SILC_SOCKET_TYPE_CLIENT:
+ {
+ SilcClientList *id_entry = NULL;
+ SilcIDListUnknown *conn_data = sock->user_data;
+
+ SILC_LOG_DEBUG(("Remote host is client"));
+
+ /* Add the client to the client ID list. We have not created the
+ client ID for the client yet. This is done when client registers
+ itself by sending NEW_CLIENT packet. */
+ silc_idlist_add_client(&server->local_list->clients,
+ NULL, NULL, NULL, NULL, NULL,
+ conn_data->send_key, conn_data->receive_key,
+ conn_data->pkcs, conn_data->hmac, &id_entry);
+
+ id_entry->hmac_key = conn_data->hmac_key;
+ id_entry->hmac_key_len = conn_data->hmac_key_len;
+ id_entry->connection = sock;
+
+ /* Free the temporary connection data context from key exchange */
+ silc_free(conn_data);
+
+ /* Mark the entry to the ID list to the socket connection for
+ fast referencing in the future. */
+ sock->user_data = (void *)id_entry;
+ break;
+ }
+ case SILC_SOCKET_TYPE_SERVER:
+ case SILC_SOCKET_TYPE_ROUTER:
+ {
+ SilcServerList *id_entry;
+ SilcIDListUnknown *conn_data = sock->user_data;
+
+ SILC_LOG_DEBUG(("Remote host is %s",
+ sock->type == SILC_SOCKET_TYPE_SERVER ?
+ "server" : "router"));
+
+ /* Add the server to the ID list. We don't have the server's ID
+ yet but we will receive it after the server sends NEW_SERVER
+ packet to us. */
+ silc_idlist_add_server(&server->local_list->servers, NULL,
+ sock->type == SILC_SOCKET_TYPE_SERVER ?
+ SILC_SERVER : SILC_ROUTER, NULL, NULL,
+ conn_data->send_key, conn_data->receive_key,
+ conn_data->pkcs, conn_data->hmac, &id_entry);
+
+ id_entry->hmac_key = conn_data->hmac_key;
+ id_entry->hmac_key_len = conn_data->hmac_key_len;
+ id_entry->connection = sock;
+
+ /* Free the temporary connection data context from key exchange */
+ silc_free(conn_data);
+
+ /* Mark the entry to the ID list to the socket connection for
+ fast referencing in the future. */
+ sock->user_data = (void *)id_entry;
+
+ /* There is connection to other server now, if it is router then
+ we will have connection to outside world. If we are router but
+ normal server connected to us then we will remain standalone,
+ if we are standlone. */
+ if (server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER) {
+ SILC_LOG_DEBUG(("We are not standalone server anymore"));
+ server->standalone = FALSE;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Connection has been fully established now. Everything is ok. */
+ SILC_LOG_DEBUG(("New connection authenticated"));
+
+ silc_protocol_free(protocol);
+ if (ctx->packet)
+ silc_buffer_free(ctx->packet);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ if (ctx->dest_id)
+ silc_free(ctx->dest_id);
+ silc_free(ctx);
+ sock->protocol = NULL;
+}
+
+typedef struct {
+ SilcPacketContext *packetdata;
+ SilcServer server;
+ SilcSocketConnection sock;
+ SilcCipher cipher;
+ SilcHmac hmac;
+} SilcServerInternalPacket;
+
+/* This function is used to read packets from network and send packets to
+ network. This is usually a generic task. */
+
+SILC_TASK_CALLBACK(silc_server_packet_process)
+{
+ SilcServer server = (SilcServer)context;
+ SilcSocketConnection sock = server->sockets[fd];
+ int ret, packetlen, paddedlen;
+
+ SILC_LOG_DEBUG(("Processing packet"));
+
+ /* Packet sending */
+ if (type == SILC_TASK_WRITE) {
+ SILC_LOG_DEBUG(("Writing data to connection"));
+
+ if (sock->outbuf->data - sock->outbuf->head)
+ silc_buffer_push(sock->outbuf,
+ sock->outbuf->data - sock->outbuf->head);
+
+ /* Write the packet out to the connection */
+ ret = silc_packet_write(fd, sock->outbuf);
+
+ /* If returned -2 could not write to connection now, will do
+ it later. */
+ if (ret == -2)
+ return;
+
+ /* Error */
+ if (ret == -1)
+ SILC_LOG_ERROR(("Could not write, packet dropped"));
+
+ /* The packet has been sent and now it is time to set the connection
+ back to only for input. When there is again some outgoing data
+ available for this connection it will be set for output as well.
+ This call clears the output setting and sets it only for input. */
+ SILC_SET_CONNECTION_FOR_INPUT(fd);
+ SILC_UNSET_OUTBUF_PENDING(sock);
+
+ silc_buffer_clear(sock->outbuf);
+ return;
+ }
+
+ /* Packet receiving */
+ if (type == SILC_TASK_READ) {
+ SILC_LOG_DEBUG(("Reading data from connection"));
+
+ /* Allocate the incoming data buffer if not done already. */
+ if (!sock->inbuf)
+ sock->inbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE);
+
+ /* Read some data from connection */
+ ret = silc_packet_read(fd, sock->inbuf);
+
+ /* If returned -2 data was not available now, will read it later. */
+ if (ret == -2)
+ return;
+
+ /* Error */
+ if (ret == -1) {
+ SILC_LOG_ERROR(("Could not read, packet dropped"));
+ return;
+ }
+
+ /* EOF */
+ if (ret == 0) {
+ SILC_LOG_DEBUG(("Read EOF"));
+
+ /* If connection is disconnecting already we will finally
+ close the connection */
+ if (SILC_IS_DISCONNECTING(sock)) {
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock);
+ silc_server_close_connection(server, sock);
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
+
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock);
+ silc_server_close_connection(server, sock);
+ return;
+ }
+
+ /* If connection is disconnecting or disconnected we will ignore
+ what we read. */
+ if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
+ SILC_LOG_DEBUG(("Ignoring read data from invalid connection"));
+ return;
+ }
+
+ /* Check whether we received a whole packet. If reading went without
+ errors we either read a whole packet or the read packet is
+ incorrect and will be dropped. */
+ SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
+ if (sock->inbuf->len < paddedlen || (packetlen < SILC_PACKET_MIN_LEN)) {
+ SILC_LOG_DEBUG(("Received incorrect packet, dropped"));
+ silc_buffer_clear(sock->inbuf);
+ silc_server_disconnect_remote(server, sock, "Incorrect packet");
+ return;
+ }
+
+ /* Decrypt a packet coming from client. */
+ if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
+ SilcClientList *clnt = (SilcClientList *)sock->user_data;
+ SilcServerInternalPacket *packet;
+ int mac_len = 0;
+
+ if (clnt->hmac)
+ mac_len = clnt->hmac->hash->hash->hash_len;
+
+ if (sock->inbuf->len - 2 > (paddedlen + mac_len)) {
+ /* Received possibly many packets at once */
+
+ while(sock->inbuf->len > 0) {
+ SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
+ if (sock->inbuf->len < paddedlen) {
+ SILC_LOG_DEBUG(("Receive incorrect packet, dropped"));
+ return;
+ }
+
+ paddedlen += 2;
+ packet = silc_calloc(1, sizeof(*packet));
+ packet->server = server;
+ packet->sock = sock;
+ packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
+ packet->packetdata->buffer = silc_buffer_alloc(paddedlen + mac_len);
+ silc_buffer_pull_tail(packet->packetdata->buffer,
+ SILC_BUFFER_END(packet->packetdata->buffer));
+ silc_buffer_put(packet->packetdata->buffer, sock->inbuf->data,
+ paddedlen + mac_len);
+ if (clnt) {
+ packet->cipher = clnt->receive_key;
+ packet->hmac = clnt->hmac;
+ }
+
+ SILC_LOG_HEXDUMP(("Incoming packet, len %d",
+ packet->packetdata->buffer->len),
+ packet->packetdata->buffer->data,
+ packet->packetdata->buffer->len);
+
+ /* Parse the packet with timeout */
+ silc_task_register(server->timeout_queue, fd,
+ silc_server_packet_parse,
+ (void *)packet, 0, 100000,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+
+ /* Pull the packet from inbuf thus we'll get the next one
+ in the inbuf. */
+ silc_buffer_pull(sock->inbuf, paddedlen);
+ if (clnt->hmac)
+ silc_buffer_pull(sock->inbuf, mac_len);
+ }
+ silc_buffer_clear(sock->inbuf);
+ return;
+ } else {
+ SILC_LOG_HEXDUMP(("An incoming packet, len %d", sock->inbuf->len),
+ sock->inbuf->data, sock->inbuf->len);
+
+ SILC_LOG_DEBUG(("Packet from client, length %d", paddedlen));
+
+ packet = silc_calloc(1, sizeof(*packet));
+ packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
+ packet->packetdata->buffer = silc_buffer_copy(sock->inbuf);
+ packet->server = server;
+ packet->sock = sock;
+ if (clnt) {
+ packet->cipher = clnt->receive_key;
+ packet->hmac = clnt->hmac;
+ }
+ silc_buffer_clear(sock->inbuf);
+
+ /* The packet is ready to be parsed now. However, this is a client
+ connection so we will parse the packet with timeout. */
+ silc_task_register(server->timeout_queue, fd,
+ silc_server_packet_parse,
+ (void *)packet, 0, 100000,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ return;
+ }
+ }
+
+ /* Decrypt a packet coming from server connection */
+ if (sock->type == SILC_SOCKET_TYPE_SERVER ||
+ sock->type == SILC_SOCKET_TYPE_ROUTER) {
+ SilcServerList *srvr = (SilcServerList *)sock->user_data;
+ SilcServerInternalPacket *packet;
+ int mac_len = 0;
+
+ if (srvr->hmac)
+ mac_len = srvr->hmac->hash->hash->hash_len;
+
+ if (sock->inbuf->len - 2 > (paddedlen + mac_len)) {
+ /* Received possibly many packets at once */
+
+ while(sock->inbuf->len > 0) {
+ SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
+ if (sock->inbuf->len < paddedlen) {
+ SILC_LOG_DEBUG(("Received incorrect packet, dropped"));
+ return;
+ }
+
+ paddedlen += 2;
+ packet = silc_calloc(1, sizeof(*packet));
+ packet->server = server;
+ packet->sock = sock;
+ packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
+ packet->packetdata->buffer = silc_buffer_alloc(paddedlen + mac_len);
+ silc_buffer_pull_tail(packet->packetdata->buffer,
+ SILC_BUFFER_END(packet->packetdata->buffer));
+ silc_buffer_put(packet->packetdata->buffer, sock->inbuf->data,
+ paddedlen + mac_len);
+ if (srvr) {
+ packet->cipher = srvr->receive_key;
+ packet->hmac = srvr->hmac;
+ }
+
+ SILC_LOG_HEXDUMP(("Incoming packet, len %d",
+ packet->packetdata->buffer->len),
+ packet->packetdata->buffer->data,
+ packet->packetdata->buffer->len);
+
+ SILC_LOG_DEBUG(("Packet from %s %s, packet length %d",
+ srvr->server_type == SILC_SERVER ?
+ "server" : "router",
+ srvr->server_name, paddedlen));
+
+ /* Parse it real soon as the packet is from server. */
+ silc_task_register(server->timeout_queue, fd,
+ silc_server_packet_parse,
+ (void *)packet, 0, 1,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+
+ /* Pull the packet from inbuf thus we'll get the next one
+ in the inbuf. */
+ silc_buffer_pull(sock->inbuf, paddedlen);
+ if (srvr->hmac)
+ silc_buffer_pull(sock->inbuf, mac_len);
+ }
+ silc_buffer_clear(sock->inbuf);
+ return;
+ } else {
+
+ SILC_LOG_HEXDUMP(("An incoming packet, len %d", sock->inbuf->len),
+ sock->inbuf->data, sock->inbuf->len);
+
+ SILC_LOG_DEBUG(("Packet from %s %s, packet length %d",
+ srvr->server_type == SILC_SERVER ?
+ "server" : "router",
+ srvr->server_name, paddedlen));
+
+ packet = silc_calloc(1, sizeof(*packet));
+ packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
+ packet->packetdata->buffer = silc_buffer_copy(sock->inbuf);
+ packet->server = server;
+ packet->sock = sock;
+ if (srvr) {
+ packet->cipher = srvr->receive_key;
+ packet->hmac = srvr->hmac;
+ }
+ silc_buffer_clear(sock->inbuf);
+
+ /* The packet is ready to be parsed now. However, this is a client
+ connection so we will parse the packet with timeout. */
+ silc_task_register(server->timeout_queue, fd,
+ silc_server_packet_parse,
+ (void *)packet, 0, 1,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ return;
+ }
+ }
+
+ /* Decrypt a packet coming from client. */
+ if (sock->type == SILC_SOCKET_TYPE_UNKNOWN) {
+ SilcIDListUnknown *conn_data = (SilcIDListUnknown *)sock->user_data;
+ SilcServerInternalPacket *packet;
+
+ SILC_LOG_HEXDUMP(("Incoming packet, len %d", sock->inbuf->len),
+ sock->inbuf->data, sock->inbuf->len);
+
+ SILC_LOG_DEBUG(("Packet from unknown connection, length %d",
+ paddedlen));
+
+ packet = silc_calloc(1, sizeof(*packet));
+ packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
+ packet->packetdata->buffer = silc_buffer_copy(sock->inbuf);
+ packet->server = server;
+ packet->sock = sock;
+ if (conn_data) {
+ packet->cipher = conn_data->receive_key;
+ packet->hmac = conn_data->hmac;
+ }
+
+ silc_buffer_clear(sock->inbuf);
+
+ /* The packet is ready to be parsed now. However, this is unknown
+ connection so we will parse the packet with timeout. */
+ silc_task_register(server->timeout_queue, fd,
+ silc_server_packet_parse,
+ (void *)packet, 0, 100000,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ return;
+ }
+ }
+
+ SILC_LOG_ERROR(("Weird, nothing happened - ignoring"));
+}
+
+/* Checks MAC in the packet. Returns TRUE if MAC is Ok. This is called
+ after packet has been totally decrypted and parsed. */
+
+static int silc_server_packet_check_mac(SilcServer server,
+ SilcSocketConnection sock,
+ SilcBuffer buffer)
+{
+ SilcHmac hmac = NULL;
+ unsigned char *hmac_key = NULL;
+ unsigned int hmac_key_len = 0;
+ unsigned int mac_len = 0;
+
+ switch(sock->type) {
+ case SILC_SOCKET_TYPE_CLIENT:
+ if (sock->user_data) {
+ hmac = ((SilcClientList *)sock->user_data)->hmac;
+ hmac_key = ((SilcClientList *)sock->user_data)->hmac_key;
+ hmac_key_len = ((SilcClientList *)sock->user_data)->hmac_key_len;
+ }
+ break;
+ case SILC_SOCKET_TYPE_SERVER:
+ case SILC_SOCKET_TYPE_ROUTER:
+ if (sock->user_data) {
+ hmac = ((SilcServerList *)sock->user_data)->hmac;
+ hmac_key = ((SilcServerList *)sock->user_data)->hmac_key;
+ hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len;
+ }
+ break;
+ default:
+ if (sock->user_data) {
+ hmac = ((SilcIDListUnknown *)sock->user_data)->hmac;
+ hmac_key = ((SilcIDListUnknown *)sock->user_data)->hmac_key;
+ hmac_key_len = ((SilcIDListUnknown *)sock->user_data)->hmac_key_len;
+ }
+ }
+
+ /* Check MAC */
+ if (hmac) {
+ int headlen = buffer->data - buffer->head;
+ unsigned char *packet_mac, mac[32];
+
+ SILC_LOG_DEBUG(("Verifying MAC"));
+
+ mac_len = hmac->hash->hash->hash_len;
+
+ silc_buffer_push(buffer, headlen);
+
+ /* Take mac from packet */
+ packet_mac = buffer->tail;
+
+ /* Make MAC and compare */
+ memset(mac, 0, sizeof(mac));
+ silc_hmac_make_with_key(hmac,
+ buffer->data, buffer->len,
+ hmac_key, hmac_key_len, mac);
+#if 0
+ SILC_LOG_HEXDUMP(("PMAC"), packet_mac, mac_len);
+ SILC_LOG_HEXDUMP(("CMAC"), mac, mac_len);
+#endif
+ if (memcmp(mac, packet_mac, mac_len)) {
+ SILC_LOG_DEBUG(("MAC failed"));
+ return FALSE;
+ }
+
+ SILC_LOG_DEBUG(("MAC is Ok"));
+ memset(mac, 0, sizeof(mac));
+
+ silc_buffer_pull(buffer, headlen);
+ }
+
+ return TRUE;
+}
+
+/* Decrypts rest of the packet (after decrypting just the SILC header).
+ After calling this function the packet is ready to be parsed by calling
+ silc_packet_parse. */
+
+static int silc_server_packet_decrypt_rest(SilcServer server,
+ SilcSocketConnection sock,
+ SilcBuffer buffer)
+{
+ SilcCipher session_key = NULL;
+ SilcHmac hmac = NULL;
+ unsigned int mac_len = 0;
+
+ switch(sock->type) {
+ case SILC_SOCKET_TYPE_CLIENT:
+ if (sock->user_data) {
+ session_key = ((SilcClientList *)sock->user_data)->receive_key;
+ hmac = ((SilcClientList *)sock->user_data)->hmac;
+ }
+ break;
+ case SILC_SOCKET_TYPE_SERVER:
+ case SILC_SOCKET_TYPE_ROUTER:
+ if (sock->user_data) {
+ session_key = ((SilcServerList *)sock->user_data)->receive_key;
+ hmac = ((SilcServerList *)sock->user_data)->hmac;
+ }
+ break;
+ default:
+ if (sock->user_data) {
+ session_key = ((SilcIDListUnknown *)sock->user_data)->receive_key;
+ hmac = ((SilcIDListUnknown *)sock->user_data)->hmac;
+ }
+ }
+
+ /* Decrypt */
+ if (session_key) {
+
+ /* Pull MAC from packet before decryption */
+ if (hmac) {
+ mac_len = hmac->hash->hash->hash_len;
+ if ((buffer->len - mac_len) > SILC_PACKET_MIN_LEN) {
+ silc_buffer_push_tail(buffer, mac_len);
+ } else {
+ SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
+ return FALSE;
+ }
+ }
+
+ SILC_LOG_DEBUG(("Decrypting rest of the packet"));
+
+ /* Decrypt rest of the packet */
+ silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
+ silc_packet_decrypt(session_key, buffer, buffer->len);
+ silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
+
+ SILC_LOG_HEXDUMP(("Fully decrypted packet, len %d", buffer->len),
+ buffer->data, buffer->len);
+ }
+
+ return TRUE;
+}
+
+/* Decrypts rest of the SILC Packet header that has been decrypted partly
+ already. This decrypts the padding of the packet also. After calling
+ this function the packet is ready to be parsed by calling function
+ silc_packet_parse. */
+
+static int silc_server_packet_decrypt_rest_special(SilcServer server,
+ SilcSocketConnection sock,
+ SilcBuffer buffer)
+{
+ SilcCipher session_key = NULL;
+ SilcHmac hmac = NULL;
+ unsigned int mac_len = 0;
+
+ switch(sock->type) {
+ case SILC_SOCKET_TYPE_CLIENT:
+ if (sock->user_data) {
+ session_key = ((SilcClientList *)sock->user_data)->receive_key;
+ hmac = ((SilcClientList *)sock->user_data)->hmac;
+ }
+ break;
+ case SILC_SOCKET_TYPE_SERVER:
+ case SILC_SOCKET_TYPE_ROUTER:
+ if (sock->user_data) {
+ session_key = ((SilcServerList *)sock->user_data)->receive_key;
+ hmac = ((SilcServerList *)sock->user_data)->hmac;
+ }
+ break;
+ default:
+ if (sock->user_data) {
+ session_key = ((SilcIDListUnknown *)sock->user_data)->receive_key;
+ hmac = ((SilcIDListUnknown *)sock->user_data)->hmac;
+ }
+ }
+
+ /* Decrypt rest of the header plus padding */
+ if (session_key) {
+ unsigned short truelen, len1, len2, padlen;
+
+ /* Pull MAC from packet before decryption */
+ if (hmac) {
+ mac_len = hmac->hash->hash->hash_len;
+ if ((buffer->len - mac_len) > SILC_PACKET_MIN_LEN) {
+ silc_buffer_push_tail(buffer, mac_len);
+ } else {
+ SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
+ return FALSE;
+ }
+ }
+
+ SILC_LOG_DEBUG(("Decrypting rest of the header"));
+
+ SILC_GET16_MSB(len1, &buffer->data[4]);
+ SILC_GET16_MSB(len2, &buffer->data[6]);
+
+ truelen = SILC_PACKET_HEADER_LEN + len1 + len2;
+ padlen = SILC_PACKET_PADLEN(truelen);
+ len1 = (truelen + padlen) - (SILC_PACKET_MIN_HEADER_LEN - 2);
+
+ silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
+ silc_packet_decrypt(session_key, buffer, len1);
+ silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
+ }
+
+ return TRUE;
+}
+
+/* Parses whole packet, received earlier. This packet is usually received
+ from client. */
+
+SILC_TASK_CALLBACK(silc_server_packet_parse)
+{
+ SilcServerInternalPacket *packet = (SilcServerInternalPacket *)context;
+ SilcServer server = packet->server;
+ SilcSocketConnection sock = packet->sock;
+ SilcBuffer buffer = packet->packetdata->buffer;
+ int ret;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ /* Decrypt start of the packet header */
+ if (packet->cipher)
+ silc_packet_decrypt(packet->cipher, buffer, SILC_PACKET_MIN_HEADER_LEN);
+
+ /* If the packet type is not any special type lets decrypt rest
+ of the packet here. */
+ if (buffer->data[3] != SILC_PACKET_CHANNEL_MESSAGE &&
+ buffer->data[3] != SILC_PACKET_PRIVATE_MESSAGE) {
+ normal:
+ /* Normal packet, decrypt rest of the packet */
+ if (!silc_server_packet_decrypt_rest(server, sock, buffer))
+ goto out;
+
+ /* Parse the packet. Packet type is returned. */
+ ret = silc_packet_parse(packet->packetdata);
+ if (ret == SILC_PACKET_NONE)
+ goto out;
+
+ /* Check MAC */
+ if (!silc_server_packet_check_mac(server, sock, buffer))
+ goto out;
+ } else {
+ /* If private message key is not set for private message it is
+ handled as normal packet. Go back up. */
+ if (buffer->data[3] == SILC_PACKET_PRIVATE_MESSAGE &&
+ !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
+ goto normal;
+
+ /* Packet requires special handling, decrypt rest of the header.
+ This only decrypts. This does not do any MAC checking, it must
+ be done individually later when doing the special processing. */
+ silc_server_packet_decrypt_rest_special(server, sock, buffer);
+
+ /* Parse the packet header in special way as this is "special"
+ packet type. */
+ ret = silc_packet_parse_special(packet->packetdata);
+ if (ret == SILC_PACKET_NONE)
+ goto out;
+ }
+
+ /* Parse the incoming packet type */
+ silc_server_packet_parse_type(server, sock, packet->packetdata);
+
+ out:
+ silc_buffer_clear(sock->inbuf);
+ // silc_buffer_free(packetdata->packetdata->buffer);
+ silc_free(packet->packetdata);
+ silc_free(packet);
+}
+
+/* Parses the packet type and calls what ever routines the packet type
+ requires. This is done for all incoming packets. */
+
+void silc_server_packet_parse_type(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer;
+ SilcPacketType type = packet->type;
+
+ SILC_LOG_DEBUG(("Parsing packet type %d", type));
+
+ /* Parse the packet type */
+ switch(type) {
+ case SILC_PACKET_DISCONNECT:
+ SILC_LOG_DEBUG(("Disconnect packet"));
+ break;
+ case SILC_PACKET_SUCCESS:
+ /*
+ * Success received for something. For now we can have only
+ * one protocol for connection executing at once hence this
+ * success message is for whatever protocol is executing currently.
+ */
+ SILC_LOG_DEBUG(("Success packet"));
+ if (sock->protocol) {
+ sock->protocol->execute(server->timeout_queue, 0,
+ sock->protocol, sock->sock, 0, 0);
+ }
+ break;
+ case SILC_PACKET_FAILURE:
+ SILC_LOG_DEBUG(("Failure packet"));
+ break;
+ case SILC_PACKET_REJECT:
+ SILC_LOG_DEBUG(("Reject packet"));
+ return;
+ break;
+
+ /*
+ * Channel packets
+ */
+ case SILC_PACKET_CHANNEL_MESSAGE:
+ /*
+ * Received channel message. Channel messages are special packets
+ * (although probably most common ones) hence they are handled
+ * specially.
+ */
+ SILC_LOG_DEBUG(("Channel Message packet"));
+ silc_server_channel_message(server, sock, packet);
+ break;
+
+ case SILC_PACKET_CHANNEL_KEY:
+ /*
+ * Received key for channel. As channels are created by the router
+ * the keys are as well. We will distribute the key to all of our
+ * locally connected clients on the particular channel. Router
+ * never receives this channel and thus is ignored.
+ */
+ SILC_LOG_DEBUG(("Channel Key packet"));
+ silc_server_channel_key(server, sock, packet);
+ break;
+
+ /*
+ * Command packets
+ */
+ case SILC_PACKET_COMMAND:
+ {
+ /*
+ * Recived command. Allocate command context and execute the command.
+ */
+ SilcServerCommandContext ctx;
+
+ SILC_LOG_DEBUG(("Command packet"));
+
+ /* Router cannot send command packet */
+ if (sock->type == SILC_SOCKET_TYPE_ROUTER)
+ break;
+
+ /* Allocate command context. This must be free'd by the
+ command routine receiving it. */
+ ctx = silc_calloc(1, sizeof(*ctx));
+ ctx->server = server;
+ ctx->sock = sock;
+ ctx->packet = packet; /* Save original packet */
+
+ /* Parse the command payload in the packet */
+ ctx->payload = silc_command_parse_payload(buffer);
+ if (!ctx->payload) {
+ SILC_LOG_ERROR(("Bad command payload, packet dropped"));
+ silc_free(ctx);
+ return;
+ }
+
+ /* Execute command. If this fails the packet is dropped. */
+ SILC_SERVER_COMMAND_EXEC(ctx);
+ silc_buffer_free(buffer);
+ }
+ break;
+
+ case SILC_PACKET_COMMAND_REPLY:
+ /*
+ * Received command reply packet. Servers never send commands thus
+ * they don't receive command reply packets either, except in cases
+ * where server has forwarded command packet coming from client.
+ * This must be the case here or we will ignore the packet.
+ */
+ SILC_LOG_DEBUG(("Command Reply packet"));
+ silc_server_packet_relay_command_reply(server, sock, packet);
+ break;
+
+ /*
+ * Private Message packets
+ */
+ case SILC_PACKET_PRIVATE_MESSAGE:
+ /*
+ * Received private message packet. The packet is coming from either
+ * client or server.
+ */
+ SILC_LOG_DEBUG(("Private Message packet"));
+ silc_server_private_message(server, sock, packet);
+ break;
+
+ case SILC_PACKET_PRIVATE_MESSAGE_KEY:
+ break;
+
+ /*
+ * Key Exchange protocol packets
+ */
+ case SILC_PACKET_KEY_EXCHANGE:
+ SILC_LOG_DEBUG(("KE packet"));
+ if (sock->protocol && sock->protocol->protocol->type
+ == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+
+ SilcServerKEInternalContext *proto_ctx =
+ (SilcServerKEInternalContext *)sock->protocol->context;
+
+ proto_ctx->packet = buffer;
+
+ /* Let the protocol handle the packet */
+ sock->protocol->execute(server->timeout_queue, 0,
+ sock->protocol, sock->sock, 0, 100000);
+ } else {
+ SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
+ "protocol active, packet dropped."));
+
+ /* XXX Trigger KE protocol?? Rekey actually, maybe. */
+ }
+ break;
+
+ case SILC_PACKET_KEY_EXCHANGE_1:
+ SILC_LOG_DEBUG(("KE 1 packet"));
+ if (sock->protocol && sock->protocol->protocol->type
+ == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+
+ SilcServerKEInternalContext *proto_ctx =
+ (SilcServerKEInternalContext *)sock->protocol->context;
+
+ if (proto_ctx->packet)
+ silc_buffer_free(proto_ctx->packet);
+
+ proto_ctx->packet = buffer;
+ proto_ctx->dest_id_type = packet->src_id_type;
+ proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+
+ /* Let the protocol handle the packet */
+ sock->protocol->execute(server->timeout_queue, 0,
+ sock->protocol, sock->sock,
+ 0, 100000);
+ } else {
+ SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
+ "protocol active, packet dropped."));
+ }
+ break;
+
+ case SILC_PACKET_KEY_EXCHANGE_2:
+ SILC_LOG_DEBUG(("KE 2 packet"));
+ if (sock->protocol && sock->protocol->protocol->type
+ == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+
+ SilcServerKEInternalContext *proto_ctx =
+ (SilcServerKEInternalContext *)sock->protocol->context;
+
+ if (proto_ctx->packet)
+ silc_buffer_free(proto_ctx->packet);
+
+ proto_ctx->packet = buffer;
+ proto_ctx->dest_id_type = packet->src_id_type;
+ proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+
+ /* Let the protocol handle the packet */
+ sock->protocol->execute(server->timeout_queue, 0,
+ sock->protocol, sock->sock,
+ 0, 100000);
+ } else {
+ SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
+ "protocol active, packet dropped."));
+ }
+ break;
+
+ case SILC_PACKET_CONNECTION_AUTH_REQUEST:
+ /* If we receive this packet we will send to the other end information
+ about our mandatory authentication method for the connection.
+ This packet maybe received at any time. */
+
+ /*
+ * Connection Authentication protocol packets
+ */
+ case SILC_PACKET_CONNECTION_AUTH:
+ /* Start of the authentication protocol. We receive here the
+ authentication data and will verify it. */
+ SILC_LOG_DEBUG(("Connection auth packet"));
+ if (sock->protocol && sock->protocol->protocol->type
+ == SILC_PROTOCOL_SERVER_CONNECTION_AUTH) {
+
+ SilcServerConnAuthInternalContext *proto_ctx =
+ (SilcServerConnAuthInternalContext *)sock->protocol->context;
+
+ proto_ctx->packet = buffer;
+
+ /* Let the protocol handle the packet */
+ sock->protocol->execute(server->timeout_queue, 0,
+ sock->protocol, sock->sock, 0, 0);
+ } else {
+ SILC_LOG_ERROR(("Received Connection Auth packet but no authentication "
+ "protocol active, packet dropped."));
+ }
+ break;
+
+ case SILC_PACKET_NEW_ID:
+ /*
+ * Received New ID packet. This includes some new ID that has been
+ * created. It may be for client, server or channel. This is the way
+ * to distribute information about new registered entities in the
+ * SILC network.
+ */
+ SILC_LOG_DEBUG(("New ID packet"));
+ silc_server_new_id(server, sock, packet);
+ break;
+
+ case SILC_PACKET_NEW_CLIENT:
+ /*
+ * Received new client packet. This includes client information that
+ * we will use to create initial client ID. After creating new
+ * ID we will send it to the client.
+ */
+ SILC_LOG_DEBUG(("New Client packet"));
+ silc_server_new_client(server, sock, packet);
+ break;
+
+ case SILC_PACKET_NEW_SERVER:
+ /*
+ * Received new server packet. This includes Server ID and some other
+ * information that we may save. This is after server as connected to us.
+ */
+ SILC_LOG_DEBUG(("New Server packet"));
+ silc_server_new_server(server, sock, packet);
+ break;
+
+ default:
+ SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
+ break;
+ }
+
+}
+
+/* Internal routine that sends packet or marks packet to be sent. This
+ is used directly only in special cases. Normal cases should use
+ silc_server_packet_send. Returns < 0 error. */
+
+static int silc_server_packet_send_real(SilcServer server,
+ SilcSocketConnection sock,
+ int force_send)
+{
+ /* Send now if forced to do so */
+ if (force_send == TRUE) {
+ int ret;
+ SILC_LOG_DEBUG(("Forcing packet send, packet sent immediately"));
+ ret = silc_packet_write(sock->sock, sock->outbuf);
+
+ silc_buffer_clear(sock->outbuf);
+
+ if (ret == -1)
+ SILC_LOG_ERROR(("Could not write, packet dropped"));
+ if (ret != -2) {
+ silc_buffer_clear(sock->outbuf);
+ return ret;
+ }
+
+ SILC_LOG_DEBUG(("Could not force the send, packet put to queue"));
+ }
+
+ SILC_LOG_DEBUG(("Packet in queue"));
+
+ /* Mark that there is some outgoing data available for this connection.
+ This call sets the connection both for input and output (the input
+ is set always and this call keeps the input setting, actually).
+ Actual data sending is performed by silc_server_packet_process. */
+ SILC_SET_CONNECTION_FOR_OUTPUT(sock->sock);
+
+ /* Mark to socket that data is pending in outgoing buffer. This flag
+ is needed if new data is added to the buffer before the earlier
+ put data is sent to the network. */
+ SILC_SET_OUTBUF_PENDING(sock);
+
+ return 0;
+}
+
+/* Prepare outgoing data buffer for packet sending. This is internal
+ routine and must always be called before sending any packets out. */
+
+static void silc_server_packet_send_prepare(SilcServer server,
+ SilcSocketConnection sock,
+ unsigned int header_len,
+ unsigned int padlen,
+ unsigned int data_len)
+{
+ int totlen, oldlen;
+
+ totlen = header_len + padlen + data_len;
+
+ /* Prepare the outgoing buffer for packet sending. */
+ if (!sock->outbuf) {
+ /* Allocate new buffer. This is done only once per connection. */
+ SILC_LOG_DEBUG(("Allocating outgoing data buffer"));
+
+ sock->outbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE);
+ silc_buffer_pull_tail(sock->outbuf, totlen);
+ silc_buffer_pull(sock->outbuf, header_len + padlen);
+ } else {
+ if (SILC_IS_OUTBUF_PENDING(sock)) {
+ /* There is some pending data in the buffer. */
+
+ if ((sock->outbuf->end - sock->outbuf->tail) < data_len) {
+ SILC_LOG_DEBUG(("Reallocating outgoing data buffer"));
+ /* XXX: not done yet */
+ }
+ oldlen = sock->outbuf->len;
+ silc_buffer_pull_tail(sock->outbuf, totlen);
+ silc_buffer_pull(sock->outbuf, header_len + padlen + oldlen);
+ } else {
+ /* Buffer is free for use */
+ silc_buffer_clear(sock->outbuf);
+ silc_buffer_pull_tail(sock->outbuf, totlen);
+ silc_buffer_pull(sock->outbuf, header_len + padlen);
+ }
+ }
+}
+
+/* Assembles a new packet to be sent out to network. This doesn't actually
+ send the packet but creates the packet and fills the outgoing data
+ buffer and marks the packet ready to be sent to network. However, If
+ argument force_send is TRUE the packet is sent immediately and not put
+ to queue. Normal case is that the packet is not sent immediately. */
+
+void silc_server_packet_send(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketType type,
+ SilcPacketFlags flags,
+ unsigned char *data,
+ unsigned int data_len,
+ int force_send)
+{
+ void *dst_id = NULL;
+ SilcIdType dst_id_type = SILC_ID_NONE;
+
+ /* Get data used in the packet sending, keys and stuff */
+ switch(sock->type) {
+ case SILC_SOCKET_TYPE_CLIENT:
+ if (((SilcClientList *)sock->user_data)->id) {
+ dst_id = ((SilcClientList *)sock->user_data)->id;
+ dst_id_type = SILC_ID_CLIENT;
+ }
+ break;
+ case SILC_SOCKET_TYPE_SERVER:
+ case SILC_SOCKET_TYPE_ROUTER:
+ if (((SilcServerList *)sock->user_data)->id) {
+ dst_id = ((SilcServerList *)sock->user_data)->id;
+ dst_id_type = SILC_ID_SERVER;
+ }
+ break;
+ default:
+ break;
+ }
+
+ silc_server_packet_send_dest(server, sock, type, flags, dst_id,
+ dst_id_type, data, data_len, force_send);
+}
+
+/* Assembles a new packet to be sent out to network. This doesn't actually
+ send the packet but creates the packet and fills the outgoing data
+ buffer and marks the packet ready to be sent to network. However, If
+ argument force_send is TRUE the packet is sent immediately and not put
+ to queue. Normal case is that the packet is not sent immediately.
+ Destination information is sent as argument for this function. */
+
+void silc_server_packet_send_dest(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketType type,
+ SilcPacketFlags flags,
+ void *dst_id,
+ SilcIdType dst_id_type,
+ unsigned char *data,
+ unsigned int data_len,
+ int force_send)
+{
+ SilcPacketContext packetdata;
+ SilcCipher cipher = NULL;
+ SilcHmac hmac = NULL;
+ unsigned char *hmac_key = NULL;
+ unsigned int hmac_key_len = 0;
+ unsigned char mac[32];
+ unsigned int mac_len = 0;
+ unsigned char *dst_id_data = NULL;
+ unsigned int dst_id_len = 0;
+
+ SILC_LOG_DEBUG(("Sending packet, type %d", type));
+
+ /* Get data used in the packet sending, keys and stuff */
+ switch(sock->type) {
+ case SILC_SOCKET_TYPE_CLIENT:
+ if (sock->user_data) {
+ cipher = ((SilcClientList *)sock->user_data)->send_key;
+ hmac = ((SilcClientList *)sock->user_data)->hmac;
+ if (hmac) {
+ mac_len = hmac->hash->hash->hash_len;
+ hmac_key = ((SilcClientList *)sock->user_data)->hmac_key;
+ hmac_key_len = ((SilcClientList *)sock->user_data)->hmac_key_len;
+ }
+ }
+ break;
+ case SILC_SOCKET_TYPE_SERVER:
+ case SILC_SOCKET_TYPE_ROUTER:
+ if (sock->user_data) {
+ cipher = ((SilcServerList *)sock->user_data)->send_key;
+ hmac = ((SilcServerList *)sock->user_data)->hmac;
+ if (hmac) {
+ mac_len = hmac->hash->hash->hash_len;
+ hmac_key = ((SilcServerList *)sock->user_data)->hmac_key;
+ hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len;
+ }
+ }
+ break;
+ default:
+ if (sock->user_data) {
+ /* We don't know what type of connection this is thus it must
+ be in authentication phase. */
+ cipher = ((SilcIDListUnknown *)sock->user_data)->send_key;
+ hmac = ((SilcIDListUnknown *)sock->user_data)->hmac;
+ if (hmac) {
+ mac_len = hmac->hash->hash->hash_len;
+ hmac_key = ((SilcIDListUnknown *)sock->user_data)->hmac_key;
+ hmac_key_len = ((SilcIDListUnknown *)sock->user_data)->hmac_key_len;
+ }
+ }
+ break;
+ }
+
+ if (dst_id) {
+ dst_id_data = silc_id_id2str(dst_id, dst_id_type);
+ dst_id_len = silc_id_get_len(dst_id_type);
+ }
+
+ /* Set the packet context pointers */
+ packetdata.type = type;
+ packetdata.flags = flags;
+ packetdata.src_id = silc_id_id2str(server->id, server->id_type);
+ packetdata.src_id_len = SILC_ID_SERVER_LEN;
+ packetdata.src_id_type = server->id_type;
+ packetdata.dst_id = dst_id_data;
+ packetdata.dst_id_len = dst_id_len;
+ packetdata.dst_id_type = dst_id_type;
+ packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + dst_id_len;
+ packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
+ packetdata.rng = server->rng;
+
+ /* Prepare outgoing data buffer for packet sending */
+ silc_server_packet_send_prepare(server, sock,
+ SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len +
+ packetdata.dst_id_len,
+ packetdata.padlen,
+ data_len);
+
+ SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
+
+ packetdata.buffer = sock->outbuf;
+
+ /* Put the data to the buffer */
+ if (data && data_len)
+ silc_buffer_put(sock->outbuf, data, data_len);
+
+ /* Create the outgoing packet */
+ silc_packet_assemble(&packetdata);
+
+ /* Compute MAC of the packet */
+ if (hmac) {
+ silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
+ hmac_key, hmac_key_len, mac);
+ silc_buffer_put_tail(sock->outbuf, mac, mac_len);
+ memset(mac, 0, sizeof(mac));
+ }
+
+ /* Encrypt the packet */
+ if (cipher)
+ silc_packet_encrypt(cipher, sock->outbuf, sock->outbuf->len);
+
+ /* Pull MAC into the visible data area */
+ if (hmac)
+ silc_buffer_pull_tail(sock->outbuf, mac_len);
+
+ SILC_LOG_HEXDUMP(("Outgoing packet, len %d", sock->outbuf->len),
+ sock->outbuf->data, sock->outbuf->len);
+
+ /* Now actually send the packet */
+ silc_server_packet_send_real(server, sock, force_send);
+
+ if (packetdata.src_id)
+ silc_free(packetdata.src_id);
+ if (packetdata.dst_id)
+ silc_free(packetdata.dst_id);
+}
+
+/* Forwards packet. Packets sent with this function will be marked as
+ forwarded (in the SILC header flags) so that the receiver knows that
+ we have forwarded the packet to it. Forwarded packets are handled
+ specially by the receiver as they are not destined to the receiver
+ originally. However, the receiver knows this because the forwarded
+ flag has been set (and the flag is authenticated). */
+
+void silc_server_packet_forward(SilcServer server,
+ SilcSocketConnection sock,
+ unsigned char *data, unsigned int data_len,
+ int force_send)
+{
+ SilcCipher cipher = NULL;
+ SilcHmac hmac = NULL;
+ unsigned char *hmac_key = NULL;
+ unsigned int hmac_key_len = 0;
+ unsigned char mac[32];
+ unsigned int mac_len = 0;
+
+ SILC_LOG_DEBUG(("Forwarding packet"));
+
+ /* Get data used in the packet sending, keys and stuff */
+ switch(sock->type) {
+ case SILC_SOCKET_TYPE_CLIENT:
+ if (sock->user_data) {
+ cipher = ((SilcClientList *)sock->user_data)->send_key;
+ hmac = ((SilcClientList *)sock->user_data)->hmac;
+ if (hmac) {
+ mac_len = hmac->hash->hash->hash_len;
+ hmac_key = ((SilcClientList *)sock->user_data)->hmac_key;
+ hmac_key_len = ((SilcClientList *)sock->user_data)->hmac_key_len;
+ }
+ }
+ break;
+ case SILC_SOCKET_TYPE_SERVER:
+ case SILC_SOCKET_TYPE_ROUTER:
+ if (sock->user_data) {
+ cipher = ((SilcServerList *)sock->user_data)->send_key;
+ hmac = ((SilcServerList *)sock->user_data)->hmac;
+ if (hmac) {
+ mac_len = hmac->hash->hash->hash_len;
+ hmac_key = ((SilcServerList *)sock->user_data)->hmac_key;
+ hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len;
+ }
+ }
+ break;
+ default:
+ /* We won't forward to unknown destination - keys must exist with
+ the destination before forwarding. */
+ return;
+ }
+
+ /* Prepare outgoing data buffer for packet sending */
+ silc_server_packet_send_prepare(server, sock, 0, 0, data_len);
+
+ /* Mungle the packet flags and add the FORWARDED flag */
+ if (data)
+ data[2] |= (unsigned char)SILC_PACKET_FLAG_FORWARDED;
+
+ /* Put the data to the buffer */
+ if (data && data_len)
+ silc_buffer_put(sock->outbuf, data, data_len);
+
+ /* Compute MAC of the packet */
+ if (hmac) {
+ silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
+ hmac_key, hmac_key_len, mac);
+ silc_buffer_put_tail(sock->outbuf, mac, mac_len);
+ memset(mac, 0, sizeof(mac));
+ }
+
+ /* Encrypt the packet */
+ if (cipher)
+ silc_packet_encrypt(cipher, sock->outbuf, sock->outbuf->len);
+
+ /* Pull MAC into the visible data area */
+ if (hmac)
+ silc_buffer_pull_tail(sock->outbuf, mac_len);
+
+ SILC_LOG_HEXDUMP(("Forwarded packet, len %d", sock->outbuf->len),
+ sock->outbuf->data, sock->outbuf->len);
+
+ /* Now actually send the packet */
+ silc_server_packet_send_real(server, sock, force_send);
+}
+
+/* This routine is used by the server to send packets to channel. The
+ packet sent with this function is distributed to all clients on
+ the channel. Usually this is used to send notify messages to the
+ channel, things like notify about new user joining to the channel. */
+
+void silc_server_packet_send_to_channel(SilcServer server,
+ SilcChannelList *channel,
+ unsigned char *data,
+ unsigned int data_len,
+ int force_send)
+{
+ int i;
+ SilcSocketConnection sock = NULL;
+ SilcPacketContext packetdata;
+ SilcClientList *client = NULL;
+ SilcServerList **routed = NULL;
+ unsigned int routed_count = 0;
+ unsigned char *hmac_key = NULL;
+ unsigned int hmac_key_len = 0;
+ unsigned char mac[32];
+ unsigned int mac_len = 0;
+ SilcCipher cipher;
+ SilcHmac hmac;
+ SilcBuffer payload;
+
+ SILC_LOG_DEBUG(("Sending packet to channel"));
+
+ /* Generate IV */
+ for (i = 0; i < 16; i++)
+ channel->iv[i] = silc_rng_get_byte(server->rng);
+
+ /* Encode the channel payload */
+ payload = silc_channel_encode_payload(0, "", data_len, data,
+ 16, channel->iv, server->rng);
+ if (!payload)
+ return;
+
+ /* Encrypt payload of the packet. This is encrypted with the
+ channel key. */
+ channel->channel_key->cipher->encrypt(channel->channel_key->context,
+ payload->data, payload->data,
+ payload->len - 16, /* -IV_LEN */
+ channel->iv);
+
+ /* Set the packet context pointers. */
+ packetdata.flags = 0;
+ packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
+ packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
+ packetdata.src_id_len = SILC_ID_SERVER_LEN;
+ packetdata.src_id_type = SILC_ID_SERVER;
+ packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+ packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
+ packetdata.dst_id_type = SILC_ID_CHANNEL;
+ packetdata.rng = server->rng;
+ packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len +
+ packetdata.dst_id_len));
+
+ /* If there are global users in the channel we will send the message
+ first to our router for further routing. */
+ if (server->server_type == SILC_SERVER && !server->standalone &&
+ channel->global_users) {
+ SilcServerList *router;
+
+ /* Get data used in packet header encryption, keys and stuff. */
+ router = server->id_entry->router;
+ sock = (SilcSocketConnection)router->connection;
+ cipher = router->send_key;
+ hmac = router->hmac;
+ mac_len = hmac->hash->hash->hash_len;
+ hmac_key = router->hmac_key;
+ hmac_key_len = router->hmac_key_len;
+
+ SILC_LOG_DEBUG(("Sending packet to router for routing"));
+
+ packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len;
+
+ /* Prepare outgoing data buffer for packet sending */
+ silc_server_packet_send_prepare(server, sock,
+ SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len +
+ packetdata.dst_id_len,
+ packetdata.padlen,
+ payload->len);
+ packetdata.buffer = sock->outbuf;
+
+ /* Put the original packet into the buffer. */
+ silc_buffer_put(sock->outbuf, payload->data, payload->len);
+
+ /* Create the outgoing packet */
+ silc_packet_assemble(&packetdata);
+
+ /* Compute MAC of the packet. MAC is computed from the header,
+ padding and the relayed packet. */
+ silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
+ hmac_key, hmac_key_len, mac);
+ silc_buffer_put_tail(sock->outbuf, mac, mac_len);
+ memset(mac, 0, sizeof(mac));
+
+ /* Encrypt the header and padding of the packet. This is encrypted
+ with normal session key shared with the client. */
+ silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len +
+ packetdata.padlen);
+
+ /* Pull MAC into the visible data area */
+ silc_buffer_pull_tail(sock->outbuf, mac_len);
+
+ SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len),
+ sock->outbuf->data, sock->outbuf->len);
+
+ /* Now actually send the packet */
+ silc_server_packet_send_real(server, sock, force_send);
+ }
+
+ /* Send the message to clients on the channel's client list. */
+ for (i = 0; i < channel->user_list_count; i++) {
+ client = channel->user_list[i].client;
+
+ /* If client has router set it is not locally connected client and
+ we will route the message to the router set in the client. */
+ if (client && client->router && server->server_type == SILC_ROUTER) {
+ int k;
+
+ /* Check if we have sent the packet to this route already */
+ for (k = 0; k < routed_count; k++)
+ if (routed[k] == client->router)
+ continue;
+
+ /* Get data used in packet header encryption, keys and stuff. */
+ sock = (SilcSocketConnection)client->router->connection;
+ cipher = client->router->send_key;
+ hmac = client->router->hmac;
+ mac_len = hmac->hash->hash->hash_len;
+ hmac_key = client->router->hmac_key;
+ hmac_key_len = client->router->hmac_key_len;
+
+ packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len;
+
+ /* Prepare outgoing data buffer for packet sending */
+ silc_server_packet_send_prepare(server, sock,
+ SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len +
+ packetdata.dst_id_len,
+ packetdata.padlen,
+ payload->len);
+ packetdata.buffer = sock->outbuf;
+
+ /* Put the encrypted payload data into the buffer. */
+ silc_buffer_put(sock->outbuf, payload->data, payload->len);
+
+ /* Create the outgoing packet */
+ silc_packet_assemble(&packetdata);
+
+ /* Compute MAC of the packet. MAC is computed from the header,
+ padding and the relayed packet. */
+ silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
+ hmac_key, hmac_key_len, mac);
+ silc_buffer_put_tail(sock->outbuf, mac, mac_len);
+ memset(mac, 0, sizeof(mac));
+
+ /* Encrypt the header and padding of the packet. This is encrypted
+ with normal session key shared with the client. */
+ silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len +
+ packetdata.padlen);
+
+ /* Pull MAC into the visible data area */
+ silc_buffer_pull_tail(sock->outbuf, mac_len);
+
+ SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
+ sock->outbuf->data, sock->outbuf->len);
+
+ /* Now actually send the packet */
+ silc_server_packet_send_real(server, sock, force_send);
+
+ /* We want to make sure that the packet is routed to same router
+ only once. Mark this route as sent route. */
+ k = routed_count;
+ routed = silc_realloc(routed, sizeof(*routed) * (k + 1));
+ routed[k] = client->router;
+ routed_count++;
+
+ continue;
+ }
+
+ /* Send to locally connected client */
+ if (client) {
+
+ /* XXX Check client's mode on the channel. */
+
+ /* Get data used in packet header encryption, keys and stuff. */
+ sock = (SilcSocketConnection)client->connection;
+ cipher = client->send_key;
+ hmac = client->hmac;
+ mac_len = hmac->hash->hash->hash_len;
+ hmac_key = client->hmac_key;
+ hmac_key_len = client->hmac_key_len;
+
+ packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len;
+
+ /* Prepare outgoing data buffer for packet sending */
+ silc_server_packet_send_prepare(server, sock,
+ SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len +
+ packetdata.dst_id_len,
+ packetdata.padlen,
+ payload->len);
+ packetdata.buffer = sock->outbuf;
+
+ /* Put the encrypted payload data into the buffer. */
+ silc_buffer_put(sock->outbuf, payload->data, payload->len);
+
+ /* Create the outgoing packet */
+ silc_packet_assemble(&packetdata);
+
+ /* Compute MAC of the packet. MAC is computed from the header,
+ padding and the relayed packet. */
+ silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
+ hmac_key, hmac_key_len, mac);
+ silc_buffer_put_tail(sock->outbuf, mac, mac_len);
+ memset(mac, 0, sizeof(mac));
+
+ /* Encrypt the header and padding of the packet. This is encrypted
+ with normal session key shared with the client. */
+ silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len +
+ packetdata.padlen);
+
+ /* Pull MAC into the visible data area */
+ silc_buffer_pull_tail(sock->outbuf, mac_len);
+
+ SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
+ sock->outbuf->data, sock->outbuf->len);
+
+ /* Now actually send the packet */
+ silc_server_packet_send_real(server, sock, force_send);
+ }
+ }
+
+ if (routed_count)
+ silc_free(routed);
+ silc_free(packetdata.src_id);
+ silc_free(packetdata.dst_id);
+ silc_buffer_free(payload);
+}
+
+/* This routine is explicitly used to relay messages to some channel.
+ Packets sent with this function we have received earlier and are
+ totally encrypted. This just sends the packet to all clients on
+ the channel. If the sender of the packet is someone on the channel
+ the message will not be sent to that client. The SILC Packet header
+ is encrypted with the session key shared between us and the client.
+ MAC is also computed before encrypting the header. Rest of the
+ packet will be untouched. */
+
+void silc_server_packet_relay_to_channel(SilcServer server,
+ SilcSocketConnection sender_sock,
+ SilcChannelList *channel,
+ void *sender,
+ SilcIdType sender_type,
+ unsigned char *data,
+ unsigned int data_len,
+ int force_send)
+{
+ int i, found = FALSE;
+ SilcSocketConnection sock = NULL;
+ SilcPacketContext packetdata;
+ SilcClientList *client = NULL;
+ SilcServerList **routed = NULL;
+ unsigned int routed_count = 0;
+ unsigned char *hmac_key = NULL;
+ unsigned int hmac_key_len = 0;
+ unsigned char mac[32];
+ unsigned int mac_len = 0;
+ SilcCipher cipher;
+ SilcHmac hmac;
+
+ SILC_LOG_DEBUG(("Relaying packet to channel"));
+
+ SILC_LOG_HEXDUMP(("XXX %d", data_len), data, data_len);
+
+ /* Set the packet context pointers. */
+ packetdata.flags = 0;
+ packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
+ packetdata.src_id = silc_id_id2str(sender, sender_type);
+ packetdata.src_id_len = silc_id_get_len(sender_type);
+ packetdata.src_id_type = sender_type;
+ packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+ packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
+ packetdata.dst_id_type = SILC_ID_CHANNEL;
+ packetdata.rng = server->rng;
+ packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len +
+ packetdata.dst_id_len));
+
+ /* If there are global users in the channel we will send the message
+ first to our router for further routing. */
+ if (server->server_type == SILC_SERVER && !server->standalone &&
+ channel->global_users) {
+ SilcServerList *router;
+
+ router = server->id_entry->router;
+
+ /* Check that the sender is not our router. */
+ if (sender_sock != (SilcSocketConnection)router->connection) {
+
+ /* Get data used in packet header encryption, keys and stuff. */
+ sock = (SilcSocketConnection)router->connection;
+ cipher = router->send_key;
+ hmac = router->hmac;
+ mac_len = hmac->hash->hash->hash_len;
+ hmac_key = router->hmac_key;
+ hmac_key_len = router->hmac_key_len;
+
+ SILC_LOG_DEBUG(("Sending packet to router for routing"));
+
+ packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len;
+
+ /* Prepare outgoing data buffer for packet sending */
+ silc_server_packet_send_prepare(server, sock,
+ SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len +
+ packetdata.dst_id_len,
+ packetdata.padlen,
+ data_len);
+ packetdata.buffer = sock->outbuf;
+
+ /* Put the original packet into the buffer. */
+ silc_buffer_put(sock->outbuf, data, data_len);
+
+ /* Create the outgoing packet */
+ silc_packet_assemble(&packetdata);
+
+ /* Compute MAC of the packet. MAC is computed from the header,
+ padding and the relayed packet. */
+ silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
+ hmac_key, hmac_key_len, mac);
+ silc_buffer_put_tail(sock->outbuf, mac, mac_len);
+ memset(mac, 0, sizeof(mac));
+
+ /* Encrypt the header and padding of the packet. This is encrypted
+ with normal session key shared with the client. */
+ silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len +
+ packetdata.padlen);
+
+ /* Pull MAC into the visible data area */
+ silc_buffer_pull_tail(sock->outbuf, mac_len);
+
+ SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len),
+ sock->outbuf->data, sock->outbuf->len);
+
+ /* Now actually send the packet */
+ silc_server_packet_send_real(server, sock, force_send);
+ }
+ }
+
+ /* Send the message to clients on the channel's client list. */
+ for (i = 0; i < channel->user_list_count; i++) {
+ client = channel->user_list[i].client;
+
+ if (client) {
+
+ /* If sender is one on the channel do not send it the packet. */
+ if (!found && !SILC_ID_CLIENT_COMPARE(client->id, sender)) {
+ found = TRUE;
+ continue;
+ }
+
+ /* If the client has set router it means that it is not locally
+ connected client and we won't send message to those in this
+ function (they will be routed separately by the caller). */
+ if (server->server_type == SILC_ROUTER && client->router) {
+ int k;
+
+ /* Sender maybe server as well so we want to make sure that
+ we won't send the message to the server it came from. */
+ if (!found && !SILC_ID_SERVER_COMPARE(client->router->id, sender)) {
+ found = TRUE;
+ continue;
+ }
+
+ /* Check if we have sent the packet to this route already */
+ for (k = 0; k < routed_count; k++)
+ if (routed[k] == client->router)
+ continue;
+
+ /* Get data used in packet header encryption, keys and stuff. */
+ sock = (SilcSocketConnection)client->router->connection;
+ cipher = client->router->send_key;
+ hmac = client->router->hmac;
+ mac_len = hmac->hash->hash->hash_len;
+ hmac_key = client->router->hmac_key;
+ hmac_key_len = client->router->hmac_key_len;
+
+ packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len;
+
+ /* Prepare outgoing data buffer for packet sending */
+ silc_server_packet_send_prepare(server, sock,
+ SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len +
+ packetdata.dst_id_len,
+ packetdata.padlen,
+ data_len);
+ packetdata.buffer = sock->outbuf;
+
+ /* Put the original packet into the buffer. */
+ silc_buffer_put(sock->outbuf, data, data_len);
+
+ /* Create the outgoing packet */
+ silc_packet_assemble(&packetdata);
+
+ /* Compute MAC of the packet. MAC is computed from the header,
+ padding and the relayed packet. */
+ silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
+ hmac_key, hmac_key_len, mac);
+ silc_buffer_put_tail(sock->outbuf, mac, mac_len);
+ memset(mac, 0, sizeof(mac));
+
+ /* Encrypt the header and padding of the packet. This is encrypted
+ with normal session key shared with the client. */
+ silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len +
+ packetdata.padlen);
+
+ /* Pull MAC into the visible data area */
+ silc_buffer_pull_tail(sock->outbuf, mac_len);
+
+ SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
+ sock->outbuf->data, sock->outbuf->len);
+
+ /* Now actually send the packet */
+ silc_server_packet_send_real(server, sock, force_send);
+
+ /* We want to make sure that the packet is routed to same router
+ only once. Mark this route as sent route. */
+ k = routed_count;
+ routed = silc_realloc(routed, sizeof(*routed) * (k + 1));
+ routed[k] = client->router;
+ routed_count++;
+
+ continue;
+ }
+
+ /* XXX Check client's mode on the channel. */
+
+
+ /* Get data used in packet header encryption, keys and stuff. */
+ sock = (SilcSocketConnection)client->connection;
+ cipher = client->send_key;
+ hmac = client->hmac;
+ mac_len = hmac->hash->hash->hash_len;
+ hmac_key = client->hmac_key;
+ hmac_key_len = client->hmac_key_len;
+
+ SILC_LOG_DEBUG(("Sending packet to client %s",
+ sock->hostname ? sock->hostname : sock->ip));
+
+ packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len;
+
+ /* Prepare outgoing data buffer for packet sending */
+ silc_server_packet_send_prepare(server, sock,
+ SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len +
+ packetdata.dst_id_len,
+ packetdata.padlen,
+ data_len);
+ packetdata.buffer = sock->outbuf;
+
+ /* Put the original packet into the buffer. */
+ silc_buffer_put(sock->outbuf, data, data_len);
+
+ /* Create the outgoing packet */
+ silc_packet_assemble(&packetdata);
+
+ /* Compute MAC of the packet. MAC is computed from the header,
+ padding and the relayed packet. */
+ silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
+ hmac_key, hmac_key_len, mac);
+ silc_buffer_put_tail(sock->outbuf, mac, mac_len);
+ memset(mac, 0, sizeof(mac));
+
+ /* Encrypt the header and padding of the packet. This is encrypted
+ with normal session key shared with the client. */
+ silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len +
+ packetdata.padlen);
+
+ /* Pull MAC into the visible data area */
+ silc_buffer_pull_tail(sock->outbuf, mac_len);
+
+ SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len),
+ sock->outbuf->data, sock->outbuf->len);
+
+ /* Now actually send the packet */
+ silc_server_packet_send_real(server, sock, force_send);
+ }
+ }
+
+ silc_free(packetdata.src_id);
+ silc_free(packetdata.dst_id);
+}
+
+/* Relays received command reply packet to the correct destination. The
+ destination must be one of our locally connected client or the packet
+ will be ignored. This is called when server has forwarded one of
+ client's command request to router and router has now replied to the
+ command. */
+
+void silc_server_packet_relay_command_reply(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer;
+ SilcClientList *client;
+ SilcClientID *id;
+ SilcSocketConnection dst_sock;
+ unsigned char mac[32];
+ unsigned int mac_len = 0;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ /* Source must be server or router */
+ /* XXX: actually it must be only router */
+ if (packet->src_id_type != SILC_ID_SERVER &&
+ (sock->type != SILC_SOCKET_TYPE_SERVER ||
+ sock->type != SILC_SOCKET_TYPE_ROUTER))
+ goto out;
+
+ /* Destination must be client */
+ if (packet->dst_id_type != SILC_ID_CLIENT)
+ goto out;
+
+ /* Execute command reply locally for the command */
+ silc_server_command_reply_process(server, sock, buffer);
+
+ id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
+
+ /* Destination must be one of ours */
+ client = silc_idlist_find_client_by_id(server->local_list->clients, id);
+ if (!client) {
+ silc_free(id);
+ goto out;
+ }
+
+ /* Relay the packet to the client */
+ if (client->hmac)
+ mac_len = client->hmac->hash->hash->hash_len;
+
+ dst_sock = (SilcSocketConnection)client->connection;
+
+ silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len
+ + packet->dst_id_len + packet->padlen);
+ silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
+ silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
+
+ /* Compute new HMAC */
+ if (client->hmac) {
+ memset(mac, 0, sizeof(mac));
+ silc_hmac_make_with_key(client->hmac,
+ dst_sock->outbuf->data,
+ dst_sock->outbuf->len,
+ client->hmac_key,
+ client->hmac_key_len,
+ mac);
+ silc_buffer_put_tail(dst_sock->outbuf, mac, mac_len);
+ memset(mac, 0, sizeof(mac));
+ }
+
+ /* Encrypt */
+ if (client && client->send_key)
+ silc_packet_encrypt(client->send_key, dst_sock->outbuf, buffer->len);
+
+ if (client->hmac)
+ silc_buffer_pull_tail(dst_sock->outbuf, mac_len);
+
+ /* Send the packet */
+ silc_server_packet_send_real(server, dst_sock, FALSE);
+
+ silc_free(id);
+
+ out:
+ silc_buffer_free(buffer);
+}
+
+/* Closes connection to socket connection */
+
+void silc_server_close_connection(SilcServer server,
+ SilcSocketConnection sock)
+{
+
+ SILC_LOG_DEBUG(("Closing connection %d", sock->sock));
+
+ /* We won't listen for this connection anymore */
+ silc_schedule_unset_listen_fd(sock->sock);
+
+ /* Unregister all tasks */
+ silc_task_unregister_by_fd(server->io_queue, sock->sock);
+ silc_task_unregister_by_fd(server->timeout_queue, sock->sock);
+
+ /* Close the actual connection */
+ silc_net_close_connection(sock->sock);
+ server->sockets[sock->sock] = NULL;
+ silc_socket_free(sock);
+}
+
+/* Sends disconnect message to remote connection and disconnects the
+ connection. */
+
+void silc_server_disconnect_remote(SilcServer server,
+ SilcSocketConnection sock,
+ const char *fmt, ...)
+{
+ va_list ap;
+ unsigned char buf[4096];
+
+ memset(buf, 0, sizeof(buf));
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ SILC_LOG_DEBUG(("Disconnecting remote host"));
+
+ /* Notify remote end that the conversation is over. The notify message
+ is tried to be sent immediately. */
+ silc_server_packet_send(server, sock, SILC_PACKET_DISCONNECT, 0,
+ buf, strlen(buf), TRUE);
+
+ /* Mark the connection to be disconnected */
+ SILC_SET_DISCONNECTED(sock);
+ silc_server_close_connection(server, sock);
+}
+
+/* Free's user_data pointer from socket connection object. As this
+ pointer maybe anything we wil switch here to find the corrent
+ data type and free it the way it needs to be free'd. */
+
+void silc_server_free_sock_user_data(SilcServer server,
+ SilcSocketConnection sock)
+{
+ SILC_LOG_DEBUG(("Start"));
+
+#define LCC(x) server->local_list->client_cache[(x) - 32]
+#define LCCC(x) server->local_list->client_cache_count[(x) - 32]
+
+ switch(sock->type) {
+ case SILC_SOCKET_TYPE_CLIENT:
+ {
+ SilcClientList *user_data = (SilcClientList *)sock->user_data;
+
+ /* Remove client from all channels */
+ silc_server_remove_from_channels(server, sock, user_data);
+
+ /* Clear ID cache */
+ if (user_data->nickname && user_data->id)
+ silc_idcache_del_by_id(LCC(user_data->nickname[0]),
+ LCCC(user_data->nickname[0]),
+ SILC_ID_CLIENT, user_data->id);
+
+ /* Free the client entry and everything in it */
+ /* XXX must take some info to history before freeing */
+ silc_idlist_del_client(&server->local_list->clients, user_data);
+ break;
+ }
+ case SILC_SOCKET_TYPE_SERVER:
+ case SILC_SOCKET_TYPE_ROUTER:
+ {
+
+ break;
+ }
+ break;
+ default:
+ {
+ SilcIDListUnknown *user_data = (SilcIDListUnknown *)sock->user_data;
+
+ if (user_data->send_key)
+ silc_cipher_free(user_data->send_key);
+ if (user_data->receive_key)
+ silc_cipher_free(user_data->receive_key);
+ if (user_data->pkcs)
+ silc_pkcs_free(user_data->pkcs);
+ if (user_data->hmac) {
+ silc_hmac_free(user_data->hmac);
+ memset(user_data->hmac_key, 0, user_data->hmac_key_len);
+ silc_free(user_data->hmac_key);
+ }
+ silc_free(user_data);
+ break;
+ }
+ }
+
+ sock->user_data = NULL;
+#undef LCC
+#undef LCCC
+}
+
+/* Removes client from all channels it has joined. This is used when
+ client connection is disconnected. If the client on a channel
+ is last, the channel is removed as well. */
+
+void silc_server_remove_from_channels(SilcServer server,
+ SilcSocketConnection sock,
+ SilcClientList *client)
+{
+ int i, k;
+ SilcChannelList *channel;
+
+#define LCC(x) server->local_list->channel_cache[(x) - 32]
+#define LCCC(x) server->local_list->channel_cache_count[(x) - 32]
+
+ /* Remove the client from all channels. The client is removed from
+ the channels' user list. */
+ for (i = 0; i < client->channel_count; i++) {
+ channel = client->channel[i];
+ if (!channel)
+ continue;
+
+ /* Remove from channel */
+ for (k = 0; k < channel->user_list_count; k++) {
+ if (channel->user_list[k].client == client) {
+
+ /* If this client is last one on the channel the channel
+ is removed all together. */
+ if (channel->user_list_count == 1) {
+ silc_idcache_del_by_id(LCC(channel->channel_name[0]),
+ LCCC(channel->channel_name[0]),
+ SILC_ID_CHANNEL, channel->id);
+ silc_idlist_del_channel(&server->local_list->channels, channel);
+ break;
+ }
+
+ channel->user_list[k].client = NULL;
+ channel->user_list[k].mode = SILC_CHANNEL_UMODE_NONE;
+
+ /* XXX */
+ /* Send notify to channel about client leaving SILC and thus
+ the entire channel. */
+ silc_server_send_notify_to_channel(server, channel,
+ "%s has left channel %s",
+ client->nickname,
+ channel->channel_name);
+ }
+ }
+ }
+
+ if (client->channel_count)
+ silc_free(client->channel);
+ client->channel = NULL;
+#undef LCC
+#undef LCCC
+}
+
+/* Timeout callback. This is called if connection is idle or for some
+ other reason is not responding within some period of time. This
+ disconnects the remote end. */
+
+SILC_TASK_CALLBACK(silc_server_timeout_remote)
+{
+ SilcServer server = (SilcServer)context;
+ SilcSocketConnection sock = server->sockets[fd];
+
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Connection timeout");
+}
+
+/* Internal routine used to send (relay, route) private messages to some
+ destination. This is used to by normal server to send the message to
+ its primary route and router uses this to send it to any route it
+ wants. If the private message key does not exist then the message
+ is re-encrypted, otherwise we just pass it along. */
+static void
+silc_server_private_message_send_internal(SilcServer server,
+ SilcSocketConnection dst_sock,
+ SilcServerList *router,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer;
+
+ /* Send and re-encrypt if private messge key does not exist */
+ if ((packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) == FALSE) {
+ unsigned char mac[32];
+ unsigned int mac_len = 0;
+
+ if (router->hmac)
+ mac_len = router->hmac->hash->hash->hash_len;
+
+ silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len
+ + packet->dst_id_len + packet->padlen);
+ silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
+ silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
+
+ /* Compute new HMAC */
+ if (router->hmac) {
+ mac_len = router->hmac->hash->hash->hash_len;
+ memset(mac, 0, sizeof(mac));
+ silc_hmac_make_with_key(router->hmac,
+ dst_sock->outbuf->data,
+ dst_sock->outbuf->len,
+ router->hmac_key,
+ router->hmac_key_len,
+ mac);
+ silc_buffer_put_tail(dst_sock->outbuf, mac, mac_len);
+ memset(mac, 0, sizeof(mac));
+ }
+
+ silc_packet_encrypt(router->send_key, dst_sock->outbuf, buffer->len);
+
+ if (router->hmac)
+ silc_buffer_pull_tail(dst_sock->outbuf, mac_len);
+
+ /* Send the packet */
+ silc_server_packet_send_real(server, dst_sock, FALSE);
+
+ } else {
+ /* Key exist so just send it */
+ silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len
+ + packet->dst_id_len + packet->padlen);
+ silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
+ silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
+ silc_server_packet_send_real(server, dst_sock, FALSE);
+ }
+}
+
+/* Internal routine to send the received private message packet to
+ our locally connected client. */
+static void
+silc_server_private_message_send_local(SilcServer server,
+ SilcSocketConnection dst_sock,
+ SilcClientList *client,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer;
+
+ /* Re-encrypt packet if needed */
+ if ((packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) == FALSE) {
+ unsigned char mac[32];
+ unsigned int mac_len = 0;
+
+ if (client->hmac)
+ mac_len = client->hmac->hash->hash->hash_len;
+
+ silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len
+ + packet->dst_id_len + packet->padlen);
+ silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
+ silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
+
+ /* Compute new HMAC */
+ if (client->hmac) {
+ memset(mac, 0, sizeof(mac));
+ silc_hmac_make_with_key(client->hmac,
+ dst_sock->outbuf->data,
+ dst_sock->outbuf->len,
+ client->hmac_key,
+ client->hmac_key_len,
+ mac);
+ silc_buffer_put_tail(dst_sock->outbuf, mac, mac_len);
+ memset(mac, 0, sizeof(mac));
+ }
+
+ /* Encrypt */
+ if (client && client->send_key)
+ silc_packet_encrypt(client->send_key, dst_sock->outbuf,
+ buffer->len);
+
+ if (client->hmac)
+ silc_buffer_pull_tail(dst_sock->outbuf, mac_len);
+
+ /* Send the packet */
+ silc_server_packet_send_real(server, dst_sock, FALSE);
+ } else {
+ /* Key exist so just send it */
+ silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len
+ + packet->dst_id_len + packet->padlen);
+ silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
+ silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
+ silc_server_packet_send_real(server, dst_sock, FALSE);
+ }
+}
+
+/* Received private message. This resolves the destination of the message
+ and sends the packet. This is used by both server and router. If the
+ destination is our locally connected client this sends the packet to
+ the client. This may also send the message for further routing if
+ the destination is not in our server (or router). */
+
+void silc_server_private_message(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer;
+ SilcClientID *id;
+ SilcServerList *router;
+ SilcSocketConnection dst_sock;
+ SilcClientList *client;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (!packet->dst_id) {
+ SILC_LOG_DEBUG(("Bad Client ID in private message packet"));
+ goto err;
+ }
+
+ /* Decode destination Client ID */
+ id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
+ if (!id) {
+ SILC_LOG_DEBUG(("Could not decode destination Client ID"));
+ goto err;
+ }
+
+ /* If the destination belongs to our server we don't have to route
+ the message anywhere but to send it to the local destination. */
+ /* XXX: Should use local cache to search but the current idcache system
+ is so sucky that it cannot be used... it MUST be rewritten! Using
+ this search is probably faster than if we'd use here the current
+ idcache system. */
+ client = silc_idlist_find_client_by_id(server->local_list->clients, id);
+ if (client) {
+ /* It exists, now deliver the message to the destination */
+ dst_sock = (SilcSocketConnection)client->connection;
+
+ /* If we are router and the client has router then the client is in
+ our cell but not directly connected to us. */
+ if (server->server_type == SILC_ROUTER && client->router) {
+ silc_server_private_message_send_internal(server, dst_sock,
+ client->router, packet);
+ goto out;
+ }
+
+ /* Seems that client really is directly connected to us */
+ silc_server_private_message_send_local(server, dst_sock, client, packet);
+ goto out;
+ }
+
+ /* Destination belongs to someone not in this server. If we are normal
+ server our action is to send the packet to our router. */
+ if (server->server_type == SILC_SERVER && !server->standalone) {
+ router = server->id_entry->router;
+ dst_sock = (SilcSocketConnection)router->connection;
+
+ /* Send to primary route */
+ silc_server_private_message_send_internal(server, dst_sock, router,
+ packet);
+ goto out;
+ }
+
+ /* We are router and we will perform route lookup for the destination
+ and send the message to the correct route. */
+ if (server->server_type == SILC_ROUTER && !server->standalone) {
+
+ /* If we don't have specific route for the destination we will send
+ it to our primary route (default route). */
+ router = silc_server_route_check(id->ip.s_addr, server->id->port);
+ if (router) {
+ dst_sock = (SilcSocketConnection)router->connection;
+ } else {
+ router = server->id_entry->router;
+ dst_sock = (SilcSocketConnection)router->connection;
+ }
+
+ /* Send packet */
+ silc_server_private_message_send_internal(server, dst_sock,
+ router, packet);
+ goto out;
+ }
+
+ err:
+ silc_server_send_error(server, sock,
+ "No such nickname: Private message not sent");
+ out:
+ silc_buffer_free(buffer);
+}
+
+SilcChannelList *silc_find_channel(SilcServer server, SilcChannelID *id)
+{
+ int i;
+ SilcIDCache *id_cache;
+
+#define LCC(x) server->local_list->channel_cache[(x)]
+#define LCCC(x) server->local_list->channel_cache_count[(x)]
+
+ for (i = 0; i < 96; i++) {
+ if (LCC(i) == NULL)
+ continue;
+ if (silc_idcache_find_by_id(LCC(i), LCCC(i), (void *)id,
+ SILC_ID_CHANNEL, &id_cache))
+ return (SilcChannelList *)id_cache->context;
+ }
+
+ return NULL;
+#undef LCC
+#undef LCCC
+}
+
+/* Process received channel message. */
+
+void silc_server_channel_message(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcChannelList *channel = NULL;
+ SilcChannelID *id = NULL;
+ SilcClientID *sender;
+ SilcBuffer buffer = packet->buffer;
+
+ SILC_LOG_DEBUG(("Processing channel message"));
+
+ /* Check MAC */
+ if (!silc_server_packet_check_mac(server, sock, buffer))
+ goto out;
+
+ /* Sanity checks */
+ if (packet->dst_id_type != SILC_ID_CHANNEL) {
+ SILC_LOG_ERROR(("Received bad message for channel, dropped"));
+ SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
+ goto out;
+ }
+
+ /* Send to local clients */
+ id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
+ channel = silc_find_channel(server, id);
+ if (!channel) {
+ SILC_LOG_DEBUG(("Could not find channel"));
+ goto out;
+ }
+
+ /* Distribute the packet to our local clients. This will send the
+ packet for further routing as well, if needed. */
+ sender = silc_id_str2id(packet->src_id, packet->src_id_type);
+ silc_server_packet_relay_to_channel(server, sock, channel, sender,
+ packet->src_id_type,
+ packet->buffer->data,
+ packet->buffer->len, FALSE);
+
+ out:
+ silc_buffer_free(buffer);
+}
+
+/* Received channel key packet. We distribute the key to all of our locally
+ connected clients on the channel. Router ignores the packet. */
+
+void silc_server_channel_key(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer;
+ SilcChannelKeyPayload payload = NULL;
+ SilcChannelID *id = NULL;
+ SilcChannelList *channel;
+ SilcClientList *client;
+ unsigned char *key;
+ unsigned int key_len;
+ char *cipher;
+ int i;
+
+ if (server->server_type == SILC_ROUTER)
+ goto out;
+
+ if (packet->src_id_type != SILC_ID_SERVER &&
+ sock->type != SILC_SOCKET_TYPE_ROUTER)
+ goto out;
+
+ /* Decode channel key payload */
+ payload = silc_channel_key_parse_payload(buffer);
+ if (!payload) {
+ SILC_LOG_ERROR(("Bad channel key payload, dropped"));
+ SILC_LOG_DEBUG(("Bad channel key payload, dropped"));
+ }
+
+ /* Get channel ID */
+ id = silc_id_str2id(silc_channel_key_get_id(payload, NULL), SILC_ID_CHANNEL);
+ if (!id)
+ goto out;
+
+ /* Get the channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list->channels, id);
+ if (!channel) {
+ SILC_LOG_ERROR(("Received key for non-existent channel"));
+ SILC_LOG_DEBUG(("Received key for non-existent channel"));
+ goto out;
+ }
+
+ /* Save the key for us as well */
+ key = silc_channel_key_get_key(payload, &key_len);
+ if (!key)
+ goto out;
+ cipher = silc_channel_key_get_cipher(payload, NULL);;
+ if (!cipher)
+ goto out;
+ channel->key_len = key_len;
+ channel->key = silc_calloc(key_len, sizeof(unsigned char));
+ memcpy(channel->key, key, key_len);
+ silc_cipher_alloc(cipher, &channel->channel_key);
+ channel->channel_key->cipher->set_key(channel->channel_key->context,
+ key, key_len);
+
+ /* Distribute the key to all clients on the channel */
+ for (i = 0; i < channel->user_list_count; i++) {
+ client = channel->user_list[i].client;
+
+ if (client)
+ silc_server_packet_send_dest(server, client->connection,
+ SILC_PACKET_CHANNEL_KEY, 0,
+ client->id, SILC_ID_CLIENT,
+ buffer->data, buffer->len, FALSE);
+ }
+
+ out:
+ if (id)
+ silc_free(id);
+ if (payload)
+ silc_channel_key_free_payload(payload);
+ silc_buffer_free(buffer);
+}
+
+/* Sends error message. Error messages may or may not have any
+ implications. */
+
+void silc_server_send_error(SilcServer server,
+ SilcSocketConnection sock,
+ const char *fmt, ...)
+{
+ va_list ap;
+ unsigned char buf[4096];
+
+ memset(buf, 0, sizeof(buf));
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ silc_server_packet_send(server, sock, SILC_PACKET_ERROR, 0,
+ buf, strlen(buf), FALSE);
+}
+
+/* Sends notify message */
+
+void silc_server_send_notify(SilcServer server,
+ SilcSocketConnection sock,
+ const char *fmt, ...)
+{
+ va_list ap;
+ unsigned char buf[4096];
+
+ memset(buf, 0, sizeof(buf));
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 0,
+ buf, strlen(buf), FALSE);
+}
+
+/* Sends notify message to a channel. The notify message sent is
+ distributed to all clients on the channel. */
+
+void silc_server_send_notify_to_channel(SilcServer server,
+ SilcChannelList *channel,
+ const char *fmt, ...)
+{
+ va_list ap;
+ unsigned char buf[4096];
+
+ memset(buf, 0, sizeof(buf));
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ silc_server_packet_send_to_channel(server, channel, buf,
+ strlen(buf), FALSE);
+}
+
+/* Sends New ID Payload to remote end. The packet is used to distribute
+ information about new registered clients, servers, channel etc. usually
+ to routers so that they can keep these information up to date.
+ If the argument `broadcast' is TRUE then the packet is sent as
+ broadcast packet. */
+
+void silc_server_send_new_id(SilcServer server,
+ SilcSocketConnection sock,
+ int broadcast,
+ void *id, SilcIdType id_type,
+ unsigned int id_len)
+{
+ SilcBuffer packet;
+ unsigned char *id_string;
+
+ id_string = silc_id_id2str(id, id_type);
+ if (!id_string)
+ return;
+
+ packet = silc_buffer_alloc(2 + 2 + id_len);
+ silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+ silc_buffer_format(packet,
+ SILC_STR_UI_SHORT(id_type),
+ 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,
+ broadcast ? SILC_PACKET_FLAG_BROADCAST : 0,
+ packet->data, packet->len, FALSE);
+ silc_free(id_string);
+ silc_buffer_free(packet);
+}
+
+/* Sends Replace ID payload to remote end. This is used to replace old
+ ID with new ID sent in the packet. This is called for example when
+ user changes nickname and we create new ID for the user. If the
+ argument `broadcast' is TRUE then the packet is sent as
+ broadcast packet. */
+/* XXX It would be expected that the new id is same type as the old
+ ID. :) */
+
+void silc_server_send_replace_id(SilcServer server,
+ SilcSocketConnection sock,
+ int broadcast,
+ void *old_id, SilcIdType old_id_type,
+ unsigned int old_id_len,
+ void *new_id, SilcIdType new_id_type,
+ unsigned int new_id_len)
+{
+ SilcBuffer packet;
+ unsigned char *oid;
+ unsigned char *nid;
+
+ oid = silc_id_id2str(old_id, old_id_type);
+ if (!oid)
+ return;
+
+ nid = silc_id_id2str(new_id, new_id_type);
+ if (!nid)
+ return;
+
+ packet = silc_buffer_alloc(2 + 2 + 2 + 2 + old_id_len + new_id_len);
+ silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+ silc_buffer_format(packet,
+ SILC_STR_UI_SHORT(old_id_type),
+ SILC_STR_UI_SHORT(old_id_len),
+ SILC_STR_UI_XNSTRING(oid, old_id_len),
+ SILC_STR_UI_SHORT(new_id_type),
+ SILC_STR_UI_SHORT(new_id_len),
+ SILC_STR_UI_XNSTRING(nid, new_id_len),
+ SILC_STR_END);
+
+ silc_server_packet_send(server, sock, SILC_PACKET_REPLACE_ID,
+ broadcast ? SILC_PACKET_FLAG_BROADCAST : 0,
+ packet->data, packet->len, FALSE);
+ silc_free(oid);
+ silc_free(nid);
+ silc_buffer_free(packet);
+}
+
+/* Creates new channel. */
+
+SilcChannelList *silc_server_new_channel(SilcServer server,
+ SilcServerID *router_id,
+ char *cipher, char *channel_name)
+{
+ int i, channel_len;
+ SilcChannelID *channel_id;
+ SilcChannelList *entry;
+ SilcCipher key;
+ unsigned char channel_key[32], *id_string;
+ SilcBuffer packet;
+
+ SILC_LOG_DEBUG(("Creating new channel"));
+
+#define LCC(x) server->local_list->channel_cache[(x) - 32]
+#define LCCC(x) server->local_list->channel_cache_count[(x) - 32]
+
+ /* Create channel key */
+ for (i = 0; i < 32; i++)
+ channel_key[i] = silc_rng_get_byte(server->rng);
+
+ if (!cipher)
+ cipher = "twofish";
+
+ /* Allocate keys */
+ silc_cipher_alloc(cipher, &key);
+ key->cipher->set_key(key->context, channel_key, 16);
+
+ /* Create the channel */
+ silc_id_create_channel_id(router_id, server->rng, &channel_id);
+ silc_idlist_add_channel(&server->local_list->channels, channel_name,
+ SILC_CHANNEL_MODE_NONE, channel_id, NULL, /*XXX*/
+ key, &entry);
+ LCCC(channel_name[0]) = silc_idcache_add(&LCC(channel_name[0]),
+ LCCC(channel_name[0]),
+ channel_name, SILC_ID_CHANNEL,
+ channel_id, (void *)entry);
+ entry->key = silc_calloc(16, sizeof(*entry->key));
+ entry->key_len = 16;
+ memcpy(entry->key, channel_key, 16);
+ memset(channel_key, 0, sizeof(channel_key));
+
+ /* Notify other routers about the new channel. We send the packet
+ to our primary route. */
+ if (server->standalone == FALSE) {
+ channel_len = strlen(channel_name);
+ id_string = silc_id_id2str(entry->id, SILC_ID_CHANNEL);
+ packet = silc_buffer_alloc(2 + SILC_ID_CHANNEL_LEN);
+
+ silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+ silc_buffer_format(packet,
+ SILC_STR_UI_SHORT(channel_len),
+ SILC_STR_UI_XNSTRING(channel_name, channel_len),
+ SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
+ SILC_STR_UI_XNSTRING(id_string, SILC_ID_CHANNEL_LEN),
+ SILC_STR_END);
+
+ /* Send the packet to our router. */
+ silc_server_packet_send(server, (SilcSocketConnection)
+ server->id_entry->router->connection,
+ SILC_PACKET_NEW_CHANNEL_USER, 0,
+ packet->data, packet->len, TRUE);
+
+ silc_free(id_string);
+ silc_buffer_free(packet);
+ }
+
+#undef LCC
+#undef LCCC
+ return entry;
+}
+
+/* Create new client. This processes incoming NEW_CLIENT packet and creates
+ Client ID for the client and adds it to lists and cache. */
+
+SilcClientList *silc_server_new_client(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer;
+ SilcClientList *id_entry;
+ char *username = NULL, *realname = NULL, *id_string;
+ SilcBuffer reply;
+
+ SILC_LOG_DEBUG(("Creating new client"));
+
+ if (sock->type != SILC_SOCKET_TYPE_CLIENT)
+ return NULL;
+
+#define LCC(x) server->local_list->client_cache[(x) - 32]
+#define LCCC(x) server->local_list->client_cache_count[(x) - 32]
+
+ silc_buffer_unformat(buffer,
+ SILC_STR_UI16_STRING_ALLOC(&username),
+ SILC_STR_UI16_STRING_ALLOC(&realname),
+ SILC_STR_END);
+
+ /* Set the pointers to the client list and create new client ID */
+ id_entry = (SilcClientList *)sock->user_data;
+ id_entry->nickname = strdup(username);
+ id_entry->username = username;
+ id_entry->userinfo = realname;
+ silc_id_create_client_id(server->id, server->rng, server->md5hash,
+ username, &id_entry->id);
+
+ /* Add to client cache */
+ LCCC(username[0]) = silc_idcache_add(&LCC(username[0]),
+ LCCC(username[0]),
+ username, SILC_ID_CLIENT,
+ id_entry->id, (void *)id_entry);
+
+ /* Notify our router about new client on the SILC network */
+ if (!server->standalone)
+ silc_server_send_new_id(server, (SilcSocketConnection)
+ server->id_entry->router->connection,
+ server->server_type == SILC_SERVER ? TRUE : FALSE,
+ id_entry->id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
+
+ /* Send the new client ID to the client. */
+ id_string = silc_id_id2str(id_entry->id, SILC_ID_CLIENT);
+ reply = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_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_END);
+ silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0,
+ reply->data, reply->len, FALSE);
+ silc_free(id_string);
+ silc_buffer_free(reply);
+
+ /* Send some nice info to the client */
+ silc_server_send_notify(server, sock,
+ "Welcome to the SILC Network %s@%s",
+ username,
+ sock->hostname ? sock->hostname : sock->ip);
+ silc_server_send_notify(server, sock,
+ "Your host is %s, running version %s",
+ server->config->server_info->server_name,
+ server_version);
+ silc_server_send_notify(server, sock,
+ "Your connection is secured with %s cipher, "
+ "key length %d bits",
+ id_entry->send_key->cipher->name,
+ id_entry->send_key->cipher->key_len);
+ silc_server_send_notify(server, sock,
+ "Your current nickname is %s",
+ id_entry->nickname);
+
+ /* XXX Send motd */
+
+#undef LCC
+#undef LCCC
+ return id_entry;
+}
+
+/* Create new server. This processes incoming NEW_SERVER packet and
+ saves the received Server ID. The server is our locally connected
+ server thus we save all the information and save it to local list.
+ This funtion can be used by both normal server and router server.
+ If normal server uses this it means that its router has connected
+ to the server. If router uses this it means that one of the cell's
+ servers is connected to the router. */
+
+SilcServerList *silc_server_new_server(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer;
+ SilcServerList *id_entry;
+ unsigned char *server_name, *id_string;
+
+ SILC_LOG_DEBUG(("Creating new server"));
+
+ if (sock->type != SILC_SOCKET_TYPE_SERVER &&
+ sock->type != SILC_SOCKET_TYPE_ROUTER)
+ return NULL;
+
+#define LSC(x) server->local_list->server_cache[(x) - 32]
+#define LSCC(x) server->local_list->server_cache_count[(x) - 32]
+
+ silc_buffer_unformat(buffer,
+ SILC_STR_UI16_STRING_ALLOC(&id_string),
+ SILC_STR_UI16_STRING_ALLOC(&server_name),
+ SILC_STR_END);
+
+ /* Save ID and name */
+ id_entry = (SilcServerList *)sock->user_data;
+ id_entry->id = silc_id_str2id(id_string, SILC_ID_SERVER);
+ id_entry->server_name = server_name;
+
+ /* Add to server cache */
+ LSCC(server_name[0]) =
+ silc_idcache_add(&LSC(server_name[0]),
+ LSCC(server_name[0]),
+ server_name, SILC_ID_SERVER,
+ id_entry->id, (void *)id_entry);
+
+ /* Distribute the information about new server in the SILC network
+ to our router. If we are normal server we won't send anything
+ since this connection must be our router connection. */
+ if (server->server_type == SILC_ROUTER && !server->standalone)
+ silc_server_send_new_id(server, server->id_entry->router->connection,
+ TRUE, id_entry->id, SILC_ID_SERVER,
+ SILC_ID_SERVER_LEN);
+
+ silc_free(id_string);
+
+#undef LSC
+#undef LSCC
+ return id_entry;
+}
+
+/* Processes incoming New ID Payload. New ID Payload is used to distribute
+ information about newly registered clients, servers and created
+ channels. */
+
+void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer;
+ SilcIdType id_type;
+ unsigned char *id_string;
+ void *id;
+
+ SILC_LOG_DEBUG(("Processing new ID"));
+
+ if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+ server->server_type == SILC_SERVER)
+ return;
+
+ silc_buffer_unformat(buffer,
+ SILC_STR_UI_SHORT(&id_type),
+ SILC_STR_UI16_STRING_ALLOC(&id_string),
+ SILC_STR_END);
+
+ /* Normal server cannot have other normal server connections */
+ if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER)
+ goto out;
+
+ id = silc_id_str2id(id_string, id_type);
+ if (!id)
+ goto out;
+
+ /* XXX Do check whether the packet is coming outside the cell or
+ from someone inside the cell. If outside use global lists otherwise
+ local lists. */
+ /* XXX If using local list set the idlist->connection to the sender's
+ socket connection as it is used in packet sending */
+
+ switch(id_type) {
+ case SILC_ID_CLIENT:
+ {
+ SilcClientList *idlist;
+
+ /* Add the client to our local list. We are router and we keep
+ cell specific local database of all clients in the cell. */
+ silc_idlist_add_client(&server->local_list->clients, NULL, NULL, NULL,
+ id, sock->user_data, NULL, NULL,
+ NULL, NULL, &idlist);
+ idlist->connection = sock;
+ }
+ break;
+
+ case SILC_ID_SERVER:
+ {
+ SilcServerList *idlist;
+
+ /* Add the server to our local list. We are router and we keep
+ cell specific local database of all servers in the cell. */
+ silc_idlist_add_server(&server->local_list->servers, NULL, 0,
+ id, server->id_entry, NULL, NULL,
+ NULL, NULL, &idlist);
+ idlist->connection = sock;
+ }
+ break;
+
+ case SILC_ID_CHANNEL:
+ /* Add the channel to our local list. We are router and we keep
+ cell specific local database of all channels in the cell. */
+ silc_idlist_add_channel(&server->local_list->channels, NULL, 0,
+ id, server->id_entry, NULL, NULL);
+ break;
+
+ default:
+ goto out;
+ break;
+ }
+
+ out:
+ silc_free(id_string);
+}