+ /* Start connection authentication */
+ entry->op =
+ silc_connauth_initiator(connauth, server->server_type == SILC_SERVER ?
+ SILC_CONN_SERVER : SILC_CONN_ROUTER, auth_meth,
+ auth_data, auth_data_len,
+ silc_server_ke_auth_compl, sconn);
+}
+
+/* Function that is called when the network connection to a router has
+ been established. This will continue with the key exchange protocol
+ with the remote router. */
+
+void silc_server_start_key_exchange(SilcServerConnection sconn)
+{
+ SilcServer server = sconn->server;
+ SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
+ SilcUnknownEntry entry;
+ SilcSKEParamsStruct params;
+ SilcSKE ske;
+
+ /* Cancel any possible retry timeouts */
+ silc_schedule_task_del_by_context(server->schedule, sconn);
+
+ /* Create packet stream */
+ sconn->sock = silc_packet_stream_create(server->packet_engine,
+ server->schedule, sconn->stream);
+ if (!sconn->sock) {
+ SILC_LOG_ERROR(("Cannot connect: cannot create packet stream"));
+ silc_stream_destroy(sconn->stream);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ return;
+ }
+ server->stat.conn_num++;
+
+ /* Set source ID to packet stream */
+ if (!silc_packet_set_ids(sconn->sock, SILC_ID_SERVER, server->id,
+ 0, NULL)) {
+ silc_packet_stream_destroy(sconn->sock);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ return;
+ }
+
+ /* Create entry for remote entity */
+ entry = silc_calloc(1, sizeof(*entry));
+ if (!entry) {
+ silc_packet_stream_destroy(sconn->sock);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ return;
+ }
+ entry->server = server;
+ entry->data.sconn = sconn;
+ silc_packet_set_context(sconn->sock, entry);
+
+ SILC_LOG_DEBUG(("Created unknown connection %p", entry));
+
+ /* Set Key Exchange flags from configuration, but fall back to global
+ settings too. */
+ memset(¶ms, 0, sizeof(params));
+ SILC_GET_SKE_FLAGS(conn, params.flags);
+ if (server->config->param.key_exchange_pfs)
+ params.flags |= SILC_SKE_SP_FLAG_PFS;
+
+ /* Start SILC Key Exchange protocol */
+ SILC_LOG_DEBUG(("Starting key exchange protocol, connection %p", sconn));
+ ske = silc_ske_alloc(server->rng, server->schedule, server->repository,
+ server->public_key, server->private_key, sconn);
+ if (!ske) {
+ silc_free(entry);
+ silc_packet_stream_destroy(sconn->sock);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ return;
+ }
+ silc_ske_set_callbacks(ske, silc_server_verify_key,
+ silc_server_ke_completed, sconn->sock);
+
+ /* Start key exchange protocol */
+ params.version = silc_version_string;
+ params.timeout_secs = server->config->key_exchange_timeout;
+ entry->op = silc_ske_initiator(ske, sconn->sock, ¶ms, NULL);
+}
+
+/* Timeout callback that will be called to retry connecting to remote
+ router. This is used by both normal and router server. This will wait
+ before retrying the connecting. The timeout is generated by exponential
+ backoff algorithm. */
+
+SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
+{
+ SilcServerConnection sconn = context;
+ SilcServer server = sconn->server;
+ SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
+ SilcServerConfigConnParams *param =
+ (conn->param ? conn->param : &server->config->param);
+
+ SILC_LOG_INFO(("Retrying connecting to %s:%d", sconn->remote_host,
+ sconn->remote_port));
+
+ /* Calculate next timeout */
+ if (sconn->retry_count >= 1) {
+ sconn->retry_timeout = sconn->retry_timeout * SILC_SERVER_RETRY_MULTIPLIER;
+ if (sconn->retry_timeout > param->reconnect_interval_max)
+ sconn->retry_timeout = param->reconnect_interval_max;
+ } else {
+ sconn->retry_timeout = param->reconnect_interval;
+ }
+ sconn->retry_count++;
+ sconn->retry_timeout = sconn->retry_timeout +
+ (silc_rng_get_rn32(server->rng) % SILC_SERVER_RETRY_RANDOMIZER);
+
+ /* If we've reached max retry count, give up. */
+ if ((sconn->retry_count > param->reconnect_count) &&
+ sconn->no_reconnect) {
+ SILC_LOG_ERROR(("Could not connect, giving up"));
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Retrying connecting %d seconds", sconn->retry_timeout));
+
+ /* We will lookup a fresh pointer later */
+ silc_server_config_unref(&sconn->conn);
+
+ /* Wait before retrying */
+ silc_schedule_task_del_by_context(server->schedule, sconn);
+ silc_schedule_task_add_timeout(server->schedule, silc_server_connect_router,
+ sconn, sconn->retry_timeout, 0);
+}
+
+/* Callback for async connection to remote router */
+
+static void silc_server_connection_established(SilcNetStatus status,
+ SilcStream stream,
+ void *context)
+{
+ SilcServerConnection sconn = context;
+ SilcServer server = sconn->server;
+
+ silc_schedule_task_del_by_context(server->schedule, sconn);
+ sconn->op = NULL;
+
+ switch (status) {
+ case SILC_NET_OK:
+ SILC_LOG_DEBUG(("Connection %p to %s:%d established", sconn,
+ sconn->remote_host, sconn->remote_port));
+
+ /* Continue with key exchange protocol */
+ sconn->stream = stream;
+ silc_server_start_key_exchange(sconn);
+ break;
+
+ case SILC_NET_UNKNOWN_IP:
+ case SILC_NET_UNKNOWN_HOST:
+ SILC_LOG_ERROR(("Could not connect to %s:%d: %s",
+ sconn->remote_host, sconn->remote_port,
+ silc_net_get_error_string(status)));
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(sconn->server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ } else {
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ }
+ break;
+
+ default:
+ SILC_LOG_ERROR(("Could not connect to %s:%d: %s",
+ sconn->remote_host, sconn->remote_port,
+ silc_net_get_error_string(status)));
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(sconn->server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ } else {
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ }
+ break;
+ }
+}
+
+/* Generic routine to use connect to a router. */
+
+SILC_TASK_CALLBACK(silc_server_connect_router)
+{
+ SilcServerConnection sconn = context;
+ SilcServer server = sconn->server;
+ SilcServerConfigRouter *rconn;
+
+ silc_schedule_task_del_by_context(server->schedule, sconn);
+
+ /* Don't connect if we are shutting down. */
+ if (server->server_shutdown) {
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ return;
+ }
+
+ SILC_LOG_INFO(("Connecting to the %s %s on port %d",
+ (sconn->backup ? "backup router" : "router"),
+ sconn->remote_host, sconn->remote_port));
+
+ if (!sconn->no_conf) {
+ /* Find connection configuration */
+ rconn = silc_server_config_find_router_conn(server, sconn->remote_host,
+ sconn->remote_port);
+ if (!rconn) {
+ SILC_LOG_INFO(("Unconfigured %s connection %s:%d, cannot connect",
+ (sconn->backup ? "backup router" : "router"),
+ sconn->remote_host, sconn->remote_port));
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ return;
+ }
+ silc_server_config_ref(&sconn->conn, server->config, (void *)rconn);
+ }
+
+ /* Connect to remote host */
+ sconn->op =
+ silc_net_tcp_connect((!server->config->server_info->primary ? NULL :
+ server->config->server_info->primary->server_ip),
+ sconn->remote_host, sconn->remote_port,
+ server->schedule, silc_server_connection_established,
+ sconn);
+ if (!sconn->op) {
+ SILC_LOG_ERROR(("Could not connect to router %s:%d",
+ sconn->remote_host, sconn->remote_port));
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ return;
+ }
+
+ /* Add to connection list */
+ silc_dlist_add(server->conns, sconn);
+}
+
+/* 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 = context;
+ SilcServerConnection sconn;
+ SilcServerConfigRouter *ptr;
+ SilcServerConfigConnParams *param;
+
+ /* Don't connect if we are shutting down. */
+ if (server->server_shutdown)
+ return;
+
+ SILC_LOG_DEBUG(("We are %s",
+ (server->server_type == SILC_SERVER ?
+ "normal server" : server->server_type == SILC_ROUTER ?
+ "router" : "backup router/normal server")));
+
+ if (!server->config->routers) {
+ /* There wasn't a configured router, we will continue but we don't
+ have a connection to outside world. We will be standalone server. */
+ SILC_LOG_DEBUG(("No router(s), we are standalone"));
+ server->standalone = TRUE;
+ return;
+ }
+
+ /* Cancel any possible retry timeouts */
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_connect_router);
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_connect_to_router_retry);
+
+ /* Create the connections to all our routes */
+ for (ptr = server->config->routers; ptr; ptr = ptr->next) {
+
+ SILC_LOG_DEBUG(("%s connection [%s] %s:%d",
+ ptr->backup_router ? "Backup router" : "Router",
+ ptr->initiator ? "Initiator" : "Responder",
+ ptr->host, ptr->port));
+
+ if (server->server_type == SILC_ROUTER && ptr->backup_router &&
+ ptr->initiator == FALSE && !server->backup_router &&
+ !silc_server_config_get_backup_router(server))
+ server->wait_backup = TRUE;
+
+ if (!ptr->initiator)
+ continue;
+ if (ptr->dynamic_connection)
+ continue;
+
+ /* Check whether we are connecting or connected to this host already */
+ if (silc_server_num_sockets_by_remote(server,
+ silc_net_is_ip(ptr->host) ?
+ ptr->host : NULL,
+ silc_net_is_ip(ptr->host) ?
+ NULL : ptr->host, ptr->port,
+ SILC_CONN_ROUTER)) {
+ SILC_LOG_DEBUG(("We are already connected to %s:%d",
+ ptr->host, ptr->port));
+
+ /* If we don't have primary router and this connection is our
+ primary router we are in desync. Reconnect to the primary. */
+ if (server->standalone && !server->router) {
+ /* XXX */
+ SilcPacketStream sock;
+ SilcServerConfigRouter *primary =
+ silc_server_config_get_primary_router(server);
+ if (primary != ptr)
+ continue;
+ sock = silc_server_find_socket_by_host(server, SILC_CONN_ROUTER,
+ ptr->host, ptr->port);
+ if (!sock)
+ continue;
+ server->backup_noswitch = TRUE;
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_server_disconnect_remote(server, sock, 0, NULL);
+ server->backup_noswitch = FALSE;
+ SILC_LOG_DEBUG(("Reconnecting to primary router"));
+ } else {
+ continue;
+ }
+ }
+
+ param = (ptr->param ? ptr->param : &server->config->param);
+
+ /* Allocate connection object for hold connection specific stuff. */
+ sconn = silc_calloc(1, sizeof(*sconn));
+ if (!sconn)
+ continue;
+ sconn->server = server;
+ sconn->remote_host = strdup(ptr->host);
+ sconn->remote_port = ptr->port;
+ sconn->backup = ptr->backup_router;
+ if (sconn->backup) {
+ sconn->backup_replace_ip = strdup(ptr->backup_replace_ip);
+ sconn->backup_replace_port = ptr->backup_replace_port;
+ }
+ sconn->no_reconnect = param->reconnect_keep_trying == FALSE;
+
+ SILC_LOG_DEBUG(("Created connection %p", sconn));
+
+ if (!server->router_conn && !sconn->backup)
+ server->router_conn = sconn;
+
+ /* Connect */
+ silc_server_connect_router(server->schedule, server, SILC_TASK_EXPIRE,
+ 0, sconn);
+ }
+}
+
+
+/************************ Accepting new connection **************************/
+
+/* After this is called, server don't wait for backup router anymore.
+ This gets called automatically even after we have backup router
+ connection established. */
+
+SILC_TASK_CALLBACK(silc_server_backup_router_wait)
+{
+ SilcServer server = context;
+ server->wait_backup = FALSE;
+}
+
+/* Authentication data callback */
+
+static SilcBool
+silc_server_accept_get_auth(SilcConnAuth connauth,
+ SilcConnectionType conn_type,
+ unsigned char **passphrase,
+ SilcUInt32 *passphrase_len,
+ SilcSKR *repository,
+ void *context)
+{
+ SilcPacketStream sock = context;
+ SilcUnknownEntry entry = silc_packet_get_context(sock);
+ SilcServer server = entry->server;
+
+ SILC_LOG_DEBUG(("Remote connection type %d", conn_type));
+
+ /* Remote end is client */
+ if (conn_type == SILC_CONN_CLIENT) {
+ SilcServerConfigClient *cconfig = entry->cconfig.ref_ptr;
+ if (!cconfig)
+ return FALSE;
+
+ *passphrase = cconfig->passphrase;
+ *passphrase_len = cconfig->passphrase_len;
+ if (cconfig->publickeys)
+ *repository = server->repository;
+
+ if (cconfig->publickeys) {
+ if (server->config->prefer_passphrase_auth) {
+ *repository = NULL;
+ } else {
+ *passphrase = NULL;
+ *passphrase_len = 0;
+ }
+ }
+
+ entry->conn_type = conn_type;
+ return TRUE;
+ }
+
+ /* Remote end is server */
+ if (conn_type == SILC_CONN_SERVER) {
+ SilcServerConfigServer *sconfig;
+
+ /* If we are normal server, don't accept the connection */
+ if (server->server_type == SILC_SERVER)
+ return FALSE;
+
+ sconfig = entry->sconfig.ref_ptr;
+ if (!sconfig)
+ return FALSE;
+
+ *passphrase = sconfig->passphrase;
+ *passphrase_len = sconfig->passphrase_len;
+ if (sconfig->publickeys)
+ *repository = server->repository;
+
+ if (sconfig->publickeys) {
+ if (server->config->prefer_passphrase_auth) {
+ *repository = NULL;
+ } else {
+ *passphrase = NULL;
+ *passphrase_len = 0;
+ }
+ }
+
+ entry->conn_type = conn_type;
+ return TRUE;
+ }
+
+ /* Remote end is router */
+ if (conn_type == SILC_CONN_ROUTER) {
+ SilcServerConfigRouter *rconfig = entry->rconfig.ref_ptr;
+ if (!rconfig)
+ return FALSE;
+
+ *passphrase = rconfig->passphrase;
+ *passphrase_len = rconfig->passphrase_len;
+ if (rconfig->publickeys)
+ *repository = server->repository;
+
+ if (rconfig->publickeys) {
+ if (server->config->prefer_passphrase_auth) {
+ *repository = NULL;
+ } else {
+ *passphrase = NULL;
+ *passphrase_len = 0;
+ }
+ }
+
+ entry->conn_type = conn_type;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Authentication completion callback. */
+
+static void
+silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
+ void *context)
+{
+ SilcPacketStream sock = context;
+ SilcUnknownEntry entry = silc_packet_get_context(sock);
+ SilcIDListData idata = (SilcIDListData)entry;
+ SilcServer server = entry->server;
+ SilcServerConfigConnParams *param = &server->config->param;
+ SilcServerConnection sconn;
+ void *id_entry;
+ const char *hostname, *ip;
+ SilcUInt16 port;
+
+ entry->op = NULL;
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+ NULL, &hostname, &ip, &port);
+
+ if (success == FALSE) {
+ /* Authentication failed */
+ SILC_LOG_INFO(("Authentication failed for %s (%s) [%s]", entry->hostname,
+ entry->ip, SILC_CONNTYPE_STRING(entry->data.conn_type)));
+ server->stat.auth_failures++;
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+
+ SILC_LOG_DEBUG(("Checking whether connection is allowed"));
+
+ switch (entry->conn_type) {
+ case SILC_CONN_CLIENT:
+ {
+ SilcClientEntry client;
+ SilcServerConfigClient *conn = entry->cconfig.ref_ptr;
+
+ /* Verify whether this connection is after all allowed to connect */
+ if (!silc_server_connection_allowed(server, sock, entry->conn_type,
+ &server->config->param,
+ conn->param,
+ silc_connauth_get_ske(connauth))) {
+ server->stat.auth_failures++;
+ goto out;
+ }
+
+ /* If we are primary router and we have backup router configured
+ but it has not connected to use yet, do not accept any other
+ connection. */
+ if (server->wait_backup && server->server_type == SILC_ROUTER &&
+ !server->backup_router) {
+ SilcServerConfigRouter *router;
+ router = silc_server_config_get_backup_router(server);
+ if (router && strcmp(server->config->server_info->primary->server_ip,
+ entry->ip) &&
+ silc_server_find_socket_by_host(server,
+ SILC_CONN_SERVER,
+ router->backup_replace_ip, 0)) {
+ SILC_LOG_INFO(("Will not accept connections because we do "
+ "not have backup router connection established"));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_PERM_DENIED,
+ "We do not have connection to backup "
+ "router established, try later");
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ server->stat.auth_failures++;
+
+ /* From here on, wait 20 seconds for the backup router to appear. */
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_backup_router_wait,
+ (void *)server, 20, 0);
+ goto out;
+ }
+ }
+
+ SILC_LOG_DEBUG(("Remote host is client"));
+ SILC_LOG_INFO(("Connection %s (%s) is client", entry->hostname,
+ entry->ip));
+
+ /* Add the client to the client ID cache. The nickname and Client ID
+ and other information is created after we have received NEW_CLIENT
+ packet from client. */
+ client = silc_idlist_add_client(server->local_list,
+ NULL, NULL, NULL, NULL, NULL, sock);
+ if (!client) {
+ SILC_LOG_ERROR(("Could not add new client to cache"));
+ server->stat.auth_failures++;
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_AUTH_FAILED, NULL);
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+ entry->data.status |= SILC_IDLIST_STATUS_LOCAL;
+ entry->data.conn_type = SILC_CONN_CLIENT;
+
+ /* Statistics */
+ server->stat.my_clients++;
+ server->stat.clients++;
+ server->stat.cell_clients++;
+
+ /* Get connection parameters */
+ if (conn->param) {
+ param = conn->param;
+
+ if (!param->keepalive_secs)
+ param->keepalive_secs = server->config->param.keepalive_secs;
+
+ if (!param->qos && server->config->param.qos) {
+ param->qos = server->config->param.qos;
+ param->qos_rate_limit = server->config->param.qos_rate_limit;
+ param->qos_bytes_limit = server->config->param.qos_bytes_limit;
+ param->qos_limit_sec = server->config->param.qos_limit_sec;
+ param->qos_limit_usec = server->config->param.qos_limit_usec;
+ }
+
+ /* Check if to be anonymous connection */
+ if (param->anonymous)
+ client->mode |= SILC_UMODE_ANONYMOUS;
+ }
+
+ /* Add public key to repository */
+ SILC_LOG_DEBUG(("Add client public key to repository"));
+ if (!silc_server_get_public_key_by_client(server, client, NULL))
+ silc_skr_add_public_key_simple(server->repository,
+ entry->data.public_key,
+ SILC_SKR_USAGE_IDENTIFICATION, client,
+ NULL);
+
+ id_entry = (void *)client;
+ break;
+ }
+
+ case SILC_CONN_SERVER:
+ case SILC_CONN_ROUTER:
+ {
+ SilcServerEntry new_server;
+ SilcBool initiator = FALSE;
+ SilcBool backup_local = FALSE;
+ SilcBool backup_router = FALSE;
+ char *backup_replace_ip = NULL;
+ SilcUInt16 backup_replace_port = 0;
+ SilcServerConfigServer *srvconn = entry->sconfig.ref_ptr;
+ SilcServerConfigRouter *rconn = entry->rconfig.ref_ptr;
+
+ /* If we are backup router and this is incoming server connection
+ and we do not have connection to primary router, do not allow
+ the connection. */
+ if (server->server_type == SILC_BACKUP_ROUTER &&
+ entry->conn_type == SILC_CONN_SERVER &&
+ !SILC_PRIMARY_ROUTE(server)) {
+ SILC_LOG_INFO(("Will not accept server connection because we do "
+ "not have primary router connection established"));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_PERM_DENIED,
+ "We do not have connection to primary "
+ "router established, try later");
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ server->stat.auth_failures++;
+ goto out;
+ }
+
+ if (entry->conn_type == SILC_CONN_ROUTER) {
+ /* Verify whether this connection is after all allowed to connect */
+ if (!silc_server_connection_allowed(server, sock,
+ entry->conn_type,
+ &server->config->param,
+ rconn ? rconn->param : NULL,
+ silc_connauth_get_ske(connauth))) {
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ server->stat.auth_failures++;
+ goto out;
+ }
+
+ if (rconn) {
+ if (rconn->param) {
+ param = rconn->param;
+
+ if (!param->keepalive_secs)
+ param->keepalive_secs = server->config->param.keepalive_secs;
+
+ if (!param->qos && server->config->param.qos) {
+ param->qos = server->config->param.qos;
+ param->qos_rate_limit = server->config->param.qos_rate_limit;
+ param->qos_bytes_limit = server->config->param.qos_bytes_limit;
+ param->qos_limit_sec = server->config->param.qos_limit_sec;
+ param->qos_limit_usec = server->config->param.qos_limit_usec;
+ }
+ }
+
+ initiator = rconn->initiator;
+ backup_local = rconn->backup_local;
+ backup_router = rconn->backup_router;
+ backup_replace_ip = rconn->backup_replace_ip;
+ backup_replace_port = rconn->backup_replace_port;
+ }
+ }
+
+ if (entry->conn_type == SILC_CONN_SERVER) {
+ /* Verify whether this connection is after all allowed to connect */
+ if (!silc_server_connection_allowed(server, sock,
+ entry->conn_type,
+ &server->config->param,
+ srvconn ? srvconn->param : NULL,
+ silc_connauth_get_ske(connauth))) {
+ server->stat.auth_failures++;
+ goto out;
+ }
+ if (srvconn) {
+ if (srvconn->param) {
+ param = srvconn->param;
+
+ if (!param->keepalive_secs)
+ param->keepalive_secs = server->config->param.keepalive_secs;
+
+ if (!param->qos && server->config->param.qos) {
+ param->qos = server->config->param.qos;
+ param->qos_rate_limit = server->config->param.qos_rate_limit;
+ param->qos_bytes_limit = server->config->param.qos_bytes_limit;
+ param->qos_limit_sec = server->config->param.qos_limit_sec;
+ param->qos_limit_usec = server->config->param.qos_limit_usec;
+ }
+ }
+
+ backup_router = srvconn->backup_router;
+ }
+ }
+
+ /* If we are primary router and we have backup router configured
+ but it has not connected to use yet, do not accept any other
+ connection. */
+ if (server->wait_backup && server->server_type == SILC_ROUTER &&
+ !server->backup_router && !backup_router) {
+ SilcServerConfigRouter *router;
+ router = silc_server_config_get_backup_router(server);
+ if (router && strcmp(server->config->server_info->primary->server_ip,
+ ip) &&
+ silc_server_find_socket_by_host(server,
+ SILC_CONN_SERVER,
+ router->backup_replace_ip, 0)) {
+ SILC_LOG_INFO(("Will not accept connections because we do "
+ "not have backup router connection established"));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_PERM_DENIED,
+ "We do not have connection to backup "
+ "router established, try later");
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ server->stat.auth_failures++;
+
+ /* From here on, wait 20 seconds for the backup router to appear. */
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_backup_router_wait,
+ (void *)server, 20, 0);
+ goto out;
+ }
+ }
+
+ SILC_LOG_DEBUG(("Remote host is %s",
+ entry->conn_type == SILC_CONN_SERVER ?
+ "server" : (backup_router ?
+ "backup router" : "router")));
+ SILC_LOG_INFO(("Connection %s (%s) is %s", entry->hostname,
+ entry->ip, entry->conn_type == SILC_CONN_SERVER ?
+ "server" : (backup_router ?
+ "backup router" : "router")));
+
+ /* Add the server into server cache. The server name and Server ID
+ is updated after we have received NEW_SERVER packet from the
+ server. We mark ourselves as router for this server if we really
+ are router. */
+ new_server =
+ silc_idlist_add_server((entry->conn_type == SILC_CONN_SERVER ?
+ server->local_list : (backup_router ?
+ server->local_list :
+ server->global_list)),
+ NULL,
+ (entry->conn_type == SILC_CONN_SERVER ?
+ SILC_SERVER : SILC_ROUTER),
+ NULL,
+ (entry->conn_type == SILC_CONN_SERVER ?
+ server->id_entry : (backup_router ?
+ server->id_entry : NULL)),
+ sock);
+ if (!new_server) {
+ SILC_LOG_ERROR(("Could not add new server to cache"));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_AUTH_FAILED, NULL);
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ server->stat.auth_failures++;
+ goto out;
+ }
+ entry->data.status |= SILC_IDLIST_STATUS_LOCAL;
+ entry->data.conn_type = entry->conn_type;
+
+ id_entry = (void *)new_server;
+
+ /* If the incoming connection is router and marked as backup router
+ then add it to be one of our backups */
+ if (entry->data.conn_type == SILC_CONN_ROUTER && backup_router) {
+ /* Change it back to SERVER type since that's what it really is. */
+ if (backup_local)
+ entry->data.conn_type = SILC_CONN_SERVER;
+ new_server->server_type = SILC_BACKUP_ROUTER;
+
+ SILC_SERVER_SEND_OPERS(server, FALSE, TRUE, SILC_NOTIFY_TYPE_NONE,
+ ("Backup router %s is now online",
+ entry->hostname));
+
+ /* Remove the backup waiting with timeout */
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_backup_router_wait,
+ (void *)server, 10, 0);
+ }
+
+ /* Statistics */
+ if (entry->data.conn_type == SILC_CONN_SERVER) {
+ server->stat.my_servers++;
+ server->stat.servers++;
+ SILC_LOG_DEBUG(("my_servers %d", server->stat.my_servers));
+ } else {
+ server->stat.my_routers++;
+ server->stat.routers++;
+ SILC_LOG_DEBUG(("my_routers %d", server->stat.my_routers));
+ }
+
+ /* Check whether this connection is to be our primary router connection
+ if we do not already have the primary route. */
+ if (!backup_router &&
+ server->standalone && entry->data.conn_type == SILC_CONN_ROUTER) {
+ if (silc_server_config_is_primary_route(server) && !initiator)
+ break;
+
+ SILC_LOG_DEBUG(("We are not standalone server anymore"));
+ server->standalone = FALSE;
+ if (!server->id_entry->router) {
+ server->id_entry->router = id_entry;
+ server->router = id_entry;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ goto out;
+ break;
+ }
+
+ /* Add connection to server->conns so that we know we have connection
+ to this peer. */
+ sconn = silc_calloc(1, sizeof(*sconn));
+ sconn->server = server;
+ sconn->sock = sock;
+ sconn->remote_host = strdup(hostname);
+ sconn->remote_port = port;
+ silc_dlist_add(server->conns, sconn);
+ idata->sconn = sconn;
+ idata->last_receive = time(NULL);
+
+ /* Add the common data structure to the ID entry. */
+ silc_idlist_add_data(id_entry, (SilcIDListData)entry);
+ silc_packet_set_context(sock, id_entry);
+
+ /* Connection has been fully established now. Everything is ok. */
+ SILC_LOG_DEBUG(("New connection %p authenticated", sconn));
+
+ /* Perform Quality of Service */
+ if (param->qos)
+ silc_socket_stream_set_qos(silc_packet_stream_get_stream(sock),
+ param->qos_rate_limit, param->qos_bytes_limit,
+ param->qos_limit_sec, param->qos_limit_usec);
+
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_free(entry);
+
+ out:
+ silc_ske_free(silc_connauth_get_ske(connauth));
+ silc_connauth_free(connauth);
+}
+
+/* SKE completion callback. We set the new keys into use here. */
+
+static void
+silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status,
+ SilcSKESecurityProperties prop,
+ SilcSKEKeyMaterial keymat,
+ SilcSKERekeyMaterial rekey,
+ void *context)
+{
+ SilcPacketStream sock = context;
+ SilcUnknownEntry entry = silc_packet_get_context(sock);
+ SilcIDListData idata = (SilcIDListData)entry;
+ SilcServer server = entry->server;
+ SilcConnAuth connauth;
+ SilcCipher send_key, receive_key;
+ SilcHmac hmac_send, hmac_receive;
+ SilcHash hash;
+ unsigned char *pk;
+ SilcUInt32 pk_len;
+
+ entry->op = NULL;
+
+ if (status != SILC_SKE_STATUS_OK) {
+ /* SKE failed */
+ SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)",
+ silc_ske_map_status(status), entry->hostname, entry->ip));
+ silc_ske_free(ske);
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Setting keys into use"));
+
+ /* Set the keys into use. The data will be encrypted after this. */
+ if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
+ &hmac_send, &hmac_receive, &hash)) {
+ /* Error setting keys */
+ silc_ske_free(ske);
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ return;
+ }
+ silc_packet_set_keys(sock, send_key, receive_key, hmac_send,
+ hmac_receive, FALSE);
+
+ idata->rekey = rekey;
+ idata->public_key = silc_pkcs_public_key_copy(prop->public_key);
+ pk = silc_pkcs_public_key_encode(idata->public_key, &pk_len);
+ silc_hash_make(server->sha1hash, pk, pk_len, idata->fingerprint);
+
+ silc_hash_alloc(silc_hash_get_name(prop->hash), &idata->hash);
+
+ SILC_LOG_DEBUG(("Starting connection authentication"));
+ server->stat.auth_attempts++;
+
+ connauth = silc_connauth_alloc(server->schedule, ske,
+ server->config->conn_auth_timeout);
+ if (!connauth) {
+ /** Error allocating auth protocol */
+ silc_ske_free(ske);
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ return;
+ }
+
+ /* Start connection authentication */
+ entry->op =
+ silc_connauth_responder(connauth, silc_server_accept_get_auth,
+ silc_server_accept_auth_compl, sock);
+}
+
+/* Accept new TCP connection */
+
+static void silc_server_accept_new_connection(SilcNetStatus status,
+ SilcStream stream,
+ void *context)
+{
+ SilcServer server = context;
+ SilcPacketStream packet_stream;
+ SilcServerConfigClient *cconfig = NULL;
+ SilcServerConfigServer *sconfig = NULL;
+ SilcServerConfigRouter *rconfig = NULL;
+ SilcServerConfigDeny *deny;
+ SilcUnknownEntry entry;
+ SilcSKE ske;
+ SilcSKEParamsStruct params;
+ char *hostname, *ip;
+ SilcUInt16 port;
+
+ SILC_LOG_DEBUG(("Accepting new connection"));
+
+ /* Check for maximum allowed connections */
+ server->stat.conn_attempts++;
+ if (silc_dlist_count(server->conns) >
+ server->config->param.connections_max) {
+ SILC_LOG_ERROR(("Refusing connection, server is full"));
+ server->stat.conn_failures++;
+ silc_stream_destroy(stream);
+ return;
+ }
+
+ /* Get hostname, IP and port */
+ if (!silc_socket_stream_get_info(stream, NULL, (const char **)&hostname,
+ (const char **)&ip, &port)) {
+ /* Bad socket stream */
+ server->stat.conn_failures++;
+ silc_stream_destroy(stream);
+ return;
+ }
+
+ /* Create packet stream */
+ packet_stream = silc_packet_stream_create(server->packet_engine,
+ server->schedule, stream);
+ if (!packet_stream) {
+ SILC_LOG_ERROR(("Refusing connection, cannot create packet stream"));
+ server->stat.conn_failures++;
+ silc_stream_destroy(stream);
+ return;
+ }
+ server->stat.conn_num++;
+
+ SILC_LOG_DEBUG(("Created packet stream %p", packet_stream));
+
+ /* Set source ID to packet stream */
+ if (!silc_packet_set_ids(packet_stream, SILC_ID_SERVER, server->id,
+ 0, NULL)) {
+ /* Out of memory */
+ server->stat.conn_failures++;
+ silc_packet_stream_destroy(packet_stream);
+ return;
+ }
+
+ /* Check whether this connection is denied to connect to us. */
+ deny = silc_server_config_find_denied(server, ip);
+ if (!deny)
+ deny = silc_server_config_find_denied(server, hostname);
+ if (deny) {
+ /* The connection is denied */
+ SILC_LOG_INFO(("Connection %s (%s) is denied", hostname, ip));
+ silc_server_disconnect_remote(server, packet_stream,
+ SILC_STATUS_ERR_BANNED_FROM_SERVER,
+ deny->reason);
+ silc_server_free_sock_user_data(server, packet_stream, NULL);
+ return;
+ }
+
+ /* Check whether we have configured this sort of connection at all. We
+ have to check all configurations since we don't know what type of
+ connection this is. */
+ if (!(cconfig = silc_server_config_find_client(server, ip)))
+ cconfig = silc_server_config_find_client(server, hostname);
+ if (!(sconfig = silc_server_config_find_server_conn(server, ip)))
+ sconfig = silc_server_config_find_server_conn(server, hostname);
+ if (server->server_type == SILC_ROUTER)
+ if (!(rconfig = silc_server_config_find_router_conn(server, ip, port)))
+ rconfig = silc_server_config_find_router_conn(server, hostname, port);
+ if (!cconfig && !sconfig && !rconfig) {
+ SILC_LOG_INFO(("Connection %s (%s) is not allowed", hostname, ip));
+ server->stat.conn_failures++;
+ silc_server_disconnect_remote(server, packet_stream,
+ SILC_STATUS_ERR_BANNED_FROM_SERVER, NULL);
+ silc_server_free_sock_user_data(server, packet_stream, NULL);
+ return;
+ }
+
+ /* The connection is allowed */
+ entry = silc_calloc(1, sizeof(*entry));
+ if (!entry) {
+ server->stat.conn_failures++;
+ silc_server_disconnect_remote(server, packet_stream,
+ SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+ silc_server_free_sock_user_data(server, packet_stream, NULL);
+ return;
+ }
+ entry->hostname = hostname;
+ entry->ip = ip;
+ entry->port = port;
+ entry->server = server;
+ entry->data.conn_type = SILC_CONN_UNKNOWN;
+ silc_packet_set_context(packet_stream, entry);
+
+ SILC_LOG_DEBUG(("Created unknown connection %p", entry));
+
+ silc_server_config_ref(&entry->cconfig, server->config, cconfig);
+ silc_server_config_ref(&entry->sconfig, server->config, sconfig);
+ silc_server_config_ref(&entry->rconfig, server->config, rconfig);
+
+ /* Take flags for key exchange. Since we do not know what type of connection
+ this is, we go through all found configurations and use the global ones
+ as well. This will result always into strictest key exchange flags. */
+ memset(¶ms, 0, sizeof(params));
+ SILC_GET_SKE_FLAGS(cconfig, params.flags);
+ SILC_GET_SKE_FLAGS(sconfig, params.flags);
+ SILC_GET_SKE_FLAGS(rconfig, params.flags);
+ if (server->config->param.key_exchange_pfs)
+ params.flags |= SILC_SKE_SP_FLAG_PFS;
+
+ SILC_LOG_INFO(("Incoming connection %s (%s)", hostname, ip));
+ server->stat.conn_attempts++;