+static void silc_server_packet_parse_type(SilcServer server,
+ SilcPacketStream sock,
+ SilcPacket packet)
+{
+ SilcPacketType type = packet->type;
+ SilcIDListData idata = silc_packet_get_context(sock);
+
+ SILC_LOG_DEBUG(("Received %s packet [flags %d]",
+ silc_get_packet_name(type), packet->flags));
+
+ /* Parse the packet type */
+ switch (type) {
+ case SILC_PACKET_NOTIFY:
+ /*
+ * Received notify packet. Server can receive notify packets from
+ * router. Server then relays the notify messages to clients if needed.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ silc_server_notify_list(server, sock, packet);
+ else
+ silc_server_notify(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.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ idata->last_receive = time(NULL);
+ silc_server_private_message(server, sock, packet);
+ break;
+
+ /*
+ * Channel packets
+ */
+ case SILC_PACKET_CHANNEL_MESSAGE:
+ /*
+ * Received channel message. Channel messages are special packets
+ * (although probably most common ones) thus they are handled
+ * specially.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ idata->last_receive = time(NULL);
+ silc_server_channel_message(server, sock, packet);
+ break;
+
+ /*
+ * Command packets
+ */
+ case SILC_PACKET_COMMAND:
+ /*
+ * Recived command. Processes the command request and allocates the
+ * command context and calls the command.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ server->stat.commands_received++;
+ silc_server_command_process(server, sock, packet);
+ break;
+
+ case SILC_PACKET_COMMAND_REPLY:
+ /*
+ * Received command reply packet. Received command reply to command. It
+ * may be reply to command sent by us or reply to command sent by client
+ * that we've routed further.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ server->stat.commands_received++;
+ silc_server_command_reply(server, sock, packet);
+ break;
+
+ case SILC_PACKET_DISCONNECT:
+ {
+ SilcStatus status;
+ char *message = NULL;
+ const char *hostname, *ip;
+
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ if (silc_buffer_len(&packet->buffer) < 1)
+ break;
+
+ status = (SilcStatus)packet->buffer.data[0];
+ if (silc_buffer_len(&packet->buffer) > 1 &&
+ silc_utf8_valid(packet->buffer.data + 1,
+ silc_buffer_len(&packet->buffer) - 1))
+ message = silc_memdup(packet->buffer.data + 1,
+ silc_buffer_len(&packet->buffer) - 1);
+
+ if (!silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+ NULL, &hostname, &ip, NULL))
+ break;
+
+ SILC_LOG_INFO(("Disconnected by %s (%s): %s (%d) %s", ip, hostname,
+ silc_get_status_message(status), status,
+ message ? message : ""));
+
+ silc_free(message);
+
+ /* Do not switch to backup in case of error */
+ server->backup_noswitch = (status == SILC_STATUS_OK ? FALSE : TRUE);
+
+ /* If backup disconnected then mark that resuming will not be allowed */
+ if (server->server_type == SILC_ROUTER && !server->backup_router &&
+ idata->conn_type == SILC_CONN_SERVER) {
+ SilcServerEntry server_entry = (SilcServerEntry)idata;
+ if (server_entry->server_type == SILC_BACKUP_ROUTER)
+ server->backup_closed = TRUE;
+ }
+
+ /* Handle the disconnection from our end too */
+ if (SILC_IS_LOCAL(idata))
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_server_close_connection(server, sock);
+ server->backup_noswitch = FALSE;
+ }
+ 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.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_channel_key(server, sock, packet);
+ break;
+
+ case SILC_PACKET_PRIVATE_MESSAGE_KEY:
+ /*
+ * Private message key packet.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_private_message_key(server, sock, packet);
+ break;
+
+ case SILC_PACKET_CONNECTION_AUTH_REQUEST:
+ /*
+ * Connection authentication request packet. When 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.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_connection_auth_request(server, sock, packet);
+ 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.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ silc_server_new_id_list(server, sock, packet);
+ else
+ 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.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ 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 received after server has
+ * connected to us.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_new_server(server, sock, packet);
+ break;
+
+ case SILC_PACKET_NEW_CHANNEL:
+ /*
+ * Received new channel packet. Information about new channel in the
+ * network are distributed using this packet.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ silc_server_new_channel_list(server, sock, packet);
+ else
+ silc_server_new_channel(server, sock, packet);
+ break;
+
+ case SILC_PACKET_HEARTBEAT:
+ /*
+ * Received heartbeat.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ break;
+
+ case SILC_PACKET_KEY_AGREEMENT:
+ /*
+ * Received heartbeat.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_key_agreement(server, sock, packet);
+ break;
+
+ case SILC_PACKET_REKEY:
+ /*
+ * Received re-key packet. The sender wants to regenerate the session
+ * keys.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_rekey(server, sock, packet);
+ break;
+
+ case SILC_PACKET_FTP:
+ /* FTP packet */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_ftp(server, sock, packet);
+ break;
+
+ case SILC_PACKET_RESUME_CLIENT:
+ /* Resume client */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_resume_client(server, sock, packet);
+ break;
+
+ case SILC_PACKET_RESUME_ROUTER:
+ /* Resume router packet received. This packet is received for backup
+ router resuming protocol. */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_backup_resume_router(server, sock, packet);
+ break;
+
+ default:
+ SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
+ break;
+ }
+}
+
+/****************************** Server API **********************************/
+
+/* 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. */
+
+SilcBool silc_server_alloc(SilcServer *new_server)
+{
+ SilcServer server;
+
+ SILC_LOG_DEBUG(("Allocating new server object"));
+
+ server = silc_calloc(1, sizeof(*server));
+ if (!server)
+ return FALSE;
+ server->server_type = SILC_SERVER;
+ server->standalone = TRUE;
+ server->local_list = silc_calloc(1, sizeof(*server->local_list));
+ if (!server->local_list)
+ return FALSE;
+ server->global_list = silc_calloc(1, sizeof(*server->global_list));
+ if (!server->global_list)
+ return FALSE;
+ server->pending_commands = silc_dlist_init();
+ if (!server->pending_commands)
+ return FALSE;
+ server->listeners = silc_dlist_init();
+ if (!server->listeners)
+ return FALSE;
+ server->repository = silc_skr_alloc();
+ if (!server->repository)
+ return FALSE;
+ server->conns = silc_dlist_init();
+ if (!server->conns)
+ return FALSE;
+ server->expired_clients = silc_dlist_init();
+ if (!server->expired_clients)
+ return FALSE;
+
+ *new_server = server;
+
+ 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)
+{
+ SilcList list;
+ SilcIDCacheEntry cache;
+ SilcIDListData idata;
+
+ SILC_LOG_DEBUG(("Free server %p", server));
+
+ if (!server)
+ return;
+
+ silc_server_backup_free(server);
+ silc_server_config_unref(&server->config_ref);
+ if (server->rng)
+ silc_rng_free(server->rng);
+ if (server->public_key)
+ silc_pkcs_public_key_free(server->public_key);
+ if (server->private_key)
+ silc_pkcs_private_key_free(server->private_key);
+ if (server->pending_commands)
+ silc_dlist_uninit(server->pending_commands);
+ if (server->id_entry) {
+ if (server->id_entry->data.sconn)
+ silc_schedule_task_del_by_context(server->schedule,
+ server->id_entry->data.sconn->sock);
+ silc_idlist_del_server(server->local_list, server->id_entry);
+ }
+
+ /* Delete all channels */
+ if (silc_idcache_get_all(server->local_list->channels, &list)) {
+ silc_list_start(list);
+ while ((cache = silc_list_get(list)))
+ silc_idlist_del_channel(server->local_list, cache->context);
+ }
+ if (silc_idcache_get_all(server->global_list->channels, &list)) {
+ silc_list_start(list);
+ while ((cache = silc_list_get(list)))
+ silc_idlist_del_channel(server->global_list, cache->context);
+ }
+
+ /* Delete all clients */
+ if (silc_idcache_get_all(server->local_list->clients, &list)) {
+ silc_list_start(list);
+ while ((cache = silc_list_get(list))) {
+ silc_schedule_task_del_by_context(server->schedule, cache->context);
+ silc_idlist_del_client(server->local_list, cache->context);
+ }
+ }
+ if (silc_idcache_get_all(server->global_list->clients, &list)) {
+ silc_list_start(list);
+ while ((cache = silc_list_get(list))) {
+ silc_schedule_task_del_by_context(server->schedule, cache->context);
+ silc_idlist_del_client(server->global_list, cache->context);
+ }
+ }
+
+ /* Delete all servers */
+ if (silc_idcache_get_all(server->local_list->servers, &list)) {
+ silc_list_start(list);
+ while ((cache = silc_list_get(list))) {
+ idata = (SilcIDListData)cache->context;
+ if (idata->sconn)
+ silc_schedule_task_del_by_context(server->schedule,
+ idata->sconn->sock);
+ silc_idlist_del_server(server->local_list, cache->context);
+ }
+ }
+ if (silc_idcache_get_all(server->global_list->servers, &list)) {
+ while ((cache = silc_list_get(list))) {
+ idata = (SilcIDListData)cache->context;
+ if (idata->sconn)
+ silc_schedule_task_del_by_context(server->schedule,
+ idata->sconn->sock);
+ silc_idlist_del_server(server->global_list, cache->context);
+ }
+ }
+
+ silc_idcache_free(server->local_list->clients);
+ silc_idcache_free(server->local_list->servers);
+ silc_idcache_free(server->local_list->channels);
+ silc_idcache_free(server->global_list->clients);
+ silc_idcache_free(server->global_list->servers);
+ silc_idcache_free(server->global_list->channels);
+ silc_hash_table_free(server->watcher_list);
+ silc_hash_table_free(server->watcher_list_pk);
+ silc_hash_free(server->md5hash);
+ silc_hash_free(server->sha1hash);
+
+ silc_dlist_uninit(server->listeners);
+ silc_dlist_uninit(server->conns);
+ silc_dlist_uninit(server->expired_clients);
+ silc_skr_free(server->repository);
+ silc_packet_engine_stop(server->packet_engine);
+
+ silc_schedule_task_del_by_context(server->schedule, server);
+ silc_schedule_uninit(server->schedule);
+ server->schedule = NULL;
+
+ silc_free(server->local_list);
+ silc_free(server->global_list);
+ silc_free(server->server_name);
+ silc_free(server);
+
+ silc_hmac_unregister_all();
+ silc_hash_unregister_all();
+ silc_cipher_unregister_all();
+ silc_pkcs_unregister_all();
+}
+
+/* Creates a new server listener. */
+
+static SilcNetListener
+silc_server_listen(SilcServer server, const char *server_ip, SilcUInt16 port)
+{
+ SilcNetListener listener;
+
+ listener =
+ silc_net_tcp_create_listener(&server_ip, 1, port, TRUE,
+ server->config->require_reverse_lookup,
+ server->schedule,
+ silc_server_accept_new_connection, server);
+ if (!listener) {
+ SILC_SERVER_LOG_ERROR(("Could not create server listener: %s on %hu",
+ server_ip, port));
+ return NULL;
+ }
+
+ return listener;
+}
+
+/* Adds a secondary listener. */
+
+SilcBool silc_server_init_secondary(SilcServer server)
+{
+ SilcServerConfigServerInfoInterface *interface;
+ SilcNetListener listener;
+
+ for (interface = server->config->server_info->secondary; interface;
+ interface = interface->next) {
+ listener = silc_server_listen(server, interface->server_ip,
+ interface->port);
+ if (!listener)
+ return FALSE;
+ silc_dlist_add(server->listeners, listener);
+ }
+
+ return TRUE;
+}
+
+/* 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. */
+
+SilcBool silc_server_init(SilcServer server)
+{
+ SilcServerID *id;
+ SilcServerEntry id_entry;
+ SilcNetListener listener;
+ SilcUInt16 *port;
+ char **ip;
+
+ SILC_LOG_DEBUG(("Initializing server"));
+
+ server->starttime = time(NULL);
+
+ /* Take config object for us */
+ silc_server_config_ref(&server->config_ref, server->config,
+ server->config);
+
+#ifdef SILC_DEBUG
+ /* Set debugging on if configured */
+ if (server->config->debug_string) {
+ silc_log_debug(TRUE);
+ silc_log_set_debug_string(server->config->debug_string);
+ }
+#endif /* SILC_DEBUG */
+
+ /* Steal public and private key from the config object */
+ server->public_key = server->config->server_info->public_key;
+ server->private_key = server->config->server_info->private_key;
+ server->config->server_info->public_key = NULL;
+ server->config->server_info->private_key = NULL;
+
+ /* Register all configured ciphers, PKCS and hash functions. */
+ if (!silc_server_config_register_ciphers(server))
+ silc_cipher_register_default();
+ if (!silc_server_config_register_pkcs(server))
+ silc_pkcs_register_default();
+ if (!silc_server_config_register_hashfuncs(server))
+ silc_hash_register_default();
+ if (!silc_server_config_register_hmacs(server))
+ silc_hmac_register_default();
+
+ /* Initialize random number generator for the server. */
+ server->rng = silc_rng_alloc();
+ silc_rng_init(server->rng);
+ silc_rng_global_init(server->rng);
+
+ /* Initialize hash functions for server to use */
+ silc_hash_alloc("md5", &server->md5hash);
+ silc_hash_alloc("sha1", &server->sha1hash);
+
+ /* Initialize the scheduler */
+ server->schedule = silc_schedule_init(server->config->param.connections_max,
+ server);
+ if (!server->schedule)
+ goto err;
+
+ /* First, register log files configuration for error output */
+ silc_server_config_setlogfiles(server);
+
+ /* Initialize ID caches */
+ server->local_list->clients =
+ silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor,
+ server);
+ server->local_list->servers =
+ silc_idcache_alloc(0, SILC_ID_SERVER, silc_idlist_server_destructor,
+ server);
+ server->local_list->channels =
+ silc_idcache_alloc(0, SILC_ID_CHANNEL, silc_idlist_channel_destructor,
+ NULL);
+
+ /* These are allocated for normal server as well as these hold some
+ global information that the server has fetched from its router. For
+ router these are used as they are supposed to be used on router. */
+ server->global_list->clients =
+ silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor,
+ server);
+ server->global_list->servers =
+ silc_idcache_alloc(0, SILC_ID_SERVER, silc_idlist_server_destructor,
+ server);
+ server->global_list->channels =
+ silc_idcache_alloc(0, SILC_ID_CHANNEL, silc_idlist_channel_destructor,
+ NULL);
+
+ /* Init watcher lists */
+ server->watcher_list =
+ silc_hash_table_alloc(1, silc_hash_client_id_hash, NULL,
+ silc_hash_data_compare, (void *)CLIENTID_HASH_LEN,
+ NULL, NULL, TRUE);
+ if (!server->watcher_list)
+ goto err;
+ server->watcher_list_pk =
+ silc_hash_table_alloc(1, silc_hash_public_key, NULL,
+ silc_hash_public_key_compare, NULL,
+ NULL, NULL, TRUE);
+ if (!server->watcher_list_pk)
+ goto err;
+
+ /* Create TCP listener */
+ listener = silc_server_listen(
+ server,
+ server->config->server_info->primary == NULL ? NULL :
+ server->config->server_info->primary->server_ip,
+ server->config->server_info->primary == NULL ? 0 :
+ server->config->server_info->primary->port);
+ if (!listener)
+ goto err;
+ silc_dlist_add(server->listeners, listener);
+
+ /* Create a Server ID for the server. */
+ port = silc_net_listener_get_port(listener, NULL);
+ ip = silc_net_listener_get_ip(listener, NULL);
+ silc_id_create_server_id(server->config->server_info->primary->public_ip ?
+ server->config->server_info->primary->public_ip :
+ ip[0], port[0], server->rng, &id);
+ if (!id)
+ goto err;
+
+ silc_free(port);
+ silc_free(ip[0]);
+ silc_free(ip);
+
+ server->id = id;
+ server->server_name = server->config->server_info->server_name;
+ server->config->server_info->server_name = NULL;
+ silc_id_id2str(server->id, SILC_ID_SERVER, server->id_string,
+ sizeof(server->id_string), &server->id_string_len);
+
+ /* 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. */
+ id_entry =
+ silc_idlist_add_server(server->local_list, strdup(server->server_name),
+ server->server_type,
+ silc_id_dup(server->id, SILC_ID_SERVER),
+ NULL, NULL);
+ if (!id_entry) {
+ SILC_LOG_ERROR(("Could not add local server to cache"));
+ goto err;
+ }
+ id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+ id_entry->data.conn_type = (server->server_type == SILC_SERVER ?
+ SILC_CONN_SERVER : SILC_CONN_ROUTER);
+ server->id_entry = id_entry;
+
+ /* Create secondary TCP listeners */
+ if (silc_server_init_secondary(server) == FALSE)
+ goto err;
+
+ server->listenning = TRUE;
+
+ /* Create connections to configured routers. */
+ silc_server_create_connections(server);
+
+ /* 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) {
+ SilcServerConfigServer *ptr = server->config->servers;
+
+ server->server_type = SILC_ROUTER;
+ while (ptr) {
+ if (ptr->backup_router) {
+ server->server_type = SILC_BACKUP_ROUTER;
+ server->backup_router = TRUE;
+ server->id_entry->server_type = SILC_BACKUP_ROUTER;
+ break;
+ }
+ ptr = ptr->next;
+ }
+ }
+
+ /* If we are normal server we'll retrieve network statisticial information
+ once in a while from the router. */
+ if (server->server_type != SILC_ROUTER)
+ silc_schedule_task_add_timeout(server->schedule, silc_server_get_stats,
+ server, 10, 0);
+
+ if (server->server_type == SILC_ROUTER)
+ server->stat.routers++;
+
+ /* Start packet engine */
+ server->packet_engine =
+ silc_packet_engine_start(server->rng, server->server_type == SILC_ROUTER,
+ &silc_server_stream_cbs, server);
+ if (!server->packet_engine)
+ goto err;
+
+ /* Register client entry expiration timeout */
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_purge_expired_clients, server,
+ 120, 0);
+
+ /* Initialize HTTP server */
+ silc_server_http_init(server);
+
+ SILC_LOG_DEBUG(("Server initialized"));