SilcSimContext *sim;
#endif
- if (server->local_list)
- silc_free(server->local_list);
- if (server->global_list)
- silc_free(server->global_list);
+ silc_free(server->local_list);
+ silc_free(server->global_list);
if (server->rng)
silc_rng_free(server->rng);
+ if (server->pkcs)
+ silc_pkcs_free(server->pkcs);
+
#ifdef SILC_SIM
while ((sim = silc_dlist_get(server->sim)) != SILC_LIST_END) {
silc_dlist_del(server->sim, sim);
silc_dlist_uninit(server->sim);
#endif
- if (server->params)
- silc_free(server->params);
+ silc_free(server->params);
if (server->pending_commands)
silc_dlist_uninit(server->pending_commands);
int silc_server_init(SilcServer server)
{
- int *sock = NULL, sock_count = 0, i;
+ int *sock = NULL, sock_count, i;
SilcServerID *id;
SilcServerEntry id_entry;
SilcIDListPurge purge;
+ SilcServerConfigSectionListenPort *listen;
SILC_LOG_DEBUG(("Initializing server"));
assert(server);
/* Set log files where log message should be saved. */
server->config->server = server;
- silc_server_config_setlogfiles(server->config);
/* Register all configured ciphers, PKCS and hash functions. */
if (!silc_server_config_register_ciphers(server->config))
/* Initialize none cipher */
silc_cipher_alloc("none", &server->none_cipher);
- /* 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. */
+ /* Allocate PKCS context for local public and private keys */
+ silc_pkcs_alloc(server->public_key->name, &server->pkcs);
+ silc_pkcs_public_key_set(server->pkcs, server->public_key);
+ silc_pkcs_private_key_set(server->pkcs, server->private_key);
+
+ /* Create a listening server. Note that our server can listen on multiple
+ ports. All listeners are created here and now. */
sock_count = 0;
- while(server->config->listen_port) {
+ listen = server->config->listen_port;
+ while(listen) {
int tmp;
tmp = silc_net_create_server(server->config->listen_port->port,
- server->config->listen_port->host);
- if (tmp < 0)
+ server->config->listen_port->listener_ip);
+
+ if (tmp < 0) {
+ SILC_LOG_ERROR(("Could not create server listener: %s on %d",
+ server->config->listen_port->listener_ip,
+ server->config->listen_port->port));
goto err0;
+ }
- sock = silc_realloc(sock, (sizeof(int *) * (sock_count + 1)));
+ sock = silc_realloc(sock, sizeof(*sock) * (sock_count + 1));
sock[sock_count] = tmp;
- server->config->listen_port = server->config->listen_port->next;
sock_count++;
+ listen = listen->next;
}
/* Initialize ID caches */
silc_net_set_socket_nonblock(sock[i]);
server->sock = sock[i];
+ /* 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, NULL, &newsocket);
+ server->sockets[sock[i]] = newsocket;
+
+ /* Perform name and address lookups to resolve the listenning address
+ and port. */
+ if (!silc_net_check_local_by_sock(sock[i], &newsocket->hostname,
+ &newsocket->ip)) {
+ if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
+ !newsocket->ip) {
+ SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
+ newsocket->hostname ? newsocket->hostname :
+ newsocket->ip ? newsocket->ip : ""));
+ server->stat.conn_failures++;
+ goto err0;
+ }
+ if (!newsocket->hostname)
+ newsocket->hostname = strdup(newsocket->ip);
+ }
+ newsocket->port = silc_net_get_local_port(sock[i]);
+
/* Create a Server ID for the server. */
- silc_id_create_server_id(sock[i], server->rng, &id);
- if (!id) {
+ silc_id_create_server_id(newsocket->ip, newsocket->port, server->rng, &id);
+ if (!id)
goto err0;
- }
server->id = id;
server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
SILC_LOG_ERROR(("Could not add ourselves to cache"));
goto err0;
}
+ id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
- /* 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);
-
- server->sockets[sock[i]] = newsocket;
-
- /* Perform name and address lookups to resolve the listenning address
- and port. */
- if (!silc_net_check_local_by_sock(sock[i], &newsocket->hostname,
- &newsocket->ip)) {
- if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
- !newsocket->ip) {
- SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
- newsocket->hostname ? newsocket->hostname :
- newsocket->ip ? newsocket->ip : ""));
- server->stat.conn_failures++;
- goto err0;
- }
- if (!newsocket->hostname)
- newsocket->hostname = strdup(newsocket->ip);
- }
- newsocket->port = silc_net_get_local_port(sock[i]);
-
/* 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]];
+ newsocket->user_data = (void *)id_entry;
+ id_entry->connection = (void *)newsocket;
server->id_entry = id_entry;
}
/* Register protocols */
silc_server_protocols_register();
- /* Initialize the scheduler. This will register the task queues as well.
- 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. */
- server->schedule = silc_schedule_init(&server->io_queue,
- &server->timeout_queue,
- &server->generic_queue,
- SILC_SERVER_MAX_CONNECTIONS);
+ /* Initialize the scheduler. */
+ server->schedule = silc_schedule_init(SILC_SERVER_MAX_CONNECTIONS);
if (!server->schedule)
goto err0;
-
- /* Add the first task to the queue. This is task that is executed by
+
+ /* Add the first task to the scheduler. 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. */
- silc_task_register(server->timeout_queue, sock[0],
- silc_server_connect_to_router,
- (void *)server, 0, 1,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ silc_schedule_task_add(server->schedule, sock[0],
+ silc_server_connect_to_router,
+ (void *)server, 0, 1,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
- /* Add listener task to the queue. This task receives new connections to the
- server. This task remains on the queue until the end of the program. */
- silc_task_register(server->io_queue, sock[0],
- silc_server_accept_new_connection,
- (void *)server, 0, 0,
- SILC_TASK_FD,
- SILC_TASK_PRI_NORMAL);
+ /* Add listener task to the scheduler. This task receives new connections
+ to the server. This task remains on the queue until the end of the
+ program. */
+ silc_schedule_task_add(server->schedule, sock[0],
+ silc_server_accept_new_connection,
+ (void *)server, 0, 0,
+ SILC_TASK_FD,
+ SILC_TASK_PRI_NORMAL);
server->listenning = TRUE;
+ /* Send log file configuration */
+ silc_server_config_setlogfiles(server->config, server->schedule);
+
/* 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)
+ if (server->config->servers) {
+ SilcServerConfigSectionServerConnection *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;
+ }
+ }
/* Register the ID Cache purge task. This periodically purges the ID cache
and removes the expired cache entries. */
/* Clients local list */
purge = silc_calloc(1, sizeof(*purge));
purge->cache = server->local_list->clients;
- purge->timeout_queue = server->timeout_queue;
- silc_task_register(purge->timeout_queue, 0,
- silc_idlist_purge,
- (void *)purge, 600, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+ purge->schedule = server->schedule;
+ purge->timeout = 600;
+ silc_schedule_task_add(purge->schedule, 0,
+ silc_idlist_purge,
+ (void *)purge, purge->timeout, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
/* Clients global list */
purge = silc_calloc(1, sizeof(*purge));
purge->cache = server->global_list->clients;
- purge->timeout_queue = server->timeout_queue;
- silc_task_register(purge->timeout_queue, 0,
- silc_idlist_purge,
- (void *)purge, 300, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+ purge->schedule = server->schedule;
+ purge->timeout = 300;
+ silc_schedule_task_add(purge->schedule, 0,
+ silc_idlist_purge,
+ (void *)purge, purge->timeout, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
SILC_LOG_DEBUG(("Server initialized"));
return FALSE;
}
-/* Fork server to background and set gid+uid to non-root.
- Silcd will not run as root, so trying to set either user or group to
- root will cause silcd to exit. */
+/* Fork server to background */
void silc_server_daemonise(SilcServer server)
+{
+ int i;
+
+ SILC_LOG_DEBUG(("Forking SILC server to background"));
+
+ i = fork();
+
+ if (i < 0) {
+ SILC_LOG_DEBUG(("fork() failed, cannot proceed"));
+ exit(1);
+ }
+ else if (i) {
+ if (geteuid())
+ SILC_LOG_DEBUG(("Server started as user"));
+ else
+ SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
+ exit(0);
+ }
+ setsid();
+}
+
+/* Drop root privligies. If this cannot be done, die. */
+
+void silc_server_drop(SilcServer server)
{
/* Are we executing silcd as root or a regular user? */
- if (geteuid()==0) {
-
+ if (!geteuid()) {
struct passwd *pw;
struct group *gr;
char *user, *group;
-
+
if (!server->config->identity || !server->config->identity->user ||
!server->config->identity->group) {
fprintf(stderr, "Error:"
"\tthe server as non-root user.\n");
exit(1);
}
-
+
/* Get the values given for user and group in configuration file */
user=server->config->identity->user;
group=server->config->identity->group;
-
+
/* Check whether the user/group information is text */
if (atoi(user)!=0 || atoi(group)!=0) {
SILC_LOG_DEBUG(("Invalid user and/or group information"));
fprintf(stderr, "Please assign them as names, not numbers\n");
exit(1);
}
-
+
/* Catch the nasty incident of string "0" returning 0 from atoi */
if (strcmp("0", user)==0 || strcmp("0", group)==0) {
SILC_LOG_DEBUG(("User and/or group configured to 0. Unacceptable"));
fprintf(stderr, "User and/or group configured to 0. Exiting\n");
exit(1);
}
-
- pw=getpwnam(user);
- gr=getgrnam(group);
- if (!pw) {
+ if (!(pw=getpwnam(user))) {
fprintf(stderr, "No such user %s found\n", user);
exit(1);
}
- if (!gr) {
+ if (!(gr=getgrnam(group))) {
fprintf(stderr, "No such group %s found\n", group);
exit(1);
}
-
+
/* Check whether user and/or group is set to root. If yes, exit
immediately. Otherwise, setgid and setuid server to user.group */
if (gr->gr_gid==0 || pw->pw_uid==0) {
"\tthe server as non-root user.\n");
exit(1);
} else {
- /* Fork server to background, making it a daemon */
- if (fork()) {
- SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
- SILC_LOG_DEBUG(("Forking SILC server to background"));
- exit(0);
- }
- setsid();
-
SILC_LOG_DEBUG(("Changing to group %s", group));
- if(setgid(gr->gr_gid)==0) {
+ if (setgid(gr->gr_gid)==0) {
SILC_LOG_DEBUG(("Setgid to %s", group));
} else {
SILC_LOG_DEBUG(("Setgid to %s failed", group));
group);
exit(1);
}
- SILC_LOG_DEBUG(("Changing to user nobody"));
- if(setuid(pw->pw_uid)==0) {
+#if defined HAVE_SETGROUPS && defined HAVE_INITGROUPS
+ if (setgroups(0, NULL)!=0) {
+ SILC_LOG_DEBUG(("Setgroups to NULL failed"));
+ fprintf(stderr, "Tried to setgroups NULL but failed. Exiting\n");
+ exit(1);
+ }
+ if (initgroups(user, gr->gr_gid)!=0) {
+ SILC_LOG_DEBUG(("Initgroups to user %s (gid=%d) failed", user, gr->gr_gid));
+ fprintf(stderr, "Tried to initgroups %s (gid=%d) but no such user. Exiting\n",
+ user, gr->gr_gid);
+ exit(1);
+ }
+#endif
+ SILC_LOG_DEBUG(("Changing to user %s", user));
+ if (setuid(pw->pw_uid)==0) {
SILC_LOG_DEBUG(("Setuid to %s", user));
} else {
SILC_LOG_DEBUG(("Setuid to %s failed", user));
exit(1);
}
}
- } else {
- /* Fork server to background, making it a daemon */
- if (fork()) {
- SILC_LOG_DEBUG(("Server started as user"));
- SILC_LOG_DEBUG(("Forking SILC server to background"));
- exit(0);
- }
- setsid();
}
}
-/* 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(server->schedule);
- silc_schedule_uninit(server->schedule);
-
- silc_server_protocols_unregister();
-
- SILC_LOG_DEBUG(("Server stopped"));
-}
-
/* The heart of the server. This runs the scheduler thus runs the server.
When this returns the server has been stopped and the program will
be terminated. */
silc_schedule(server->schedule);
}
-/* 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. */
+/* 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. */
-SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
+void silc_server_stop(SilcServer server)
{
- SilcServerConnection sconn = (SilcServerConnection)context;
- SilcServer server = sconn->server;
-
- SILC_LOG_INFO(("Retrying connecting to a router"));
+ SILC_LOG_DEBUG(("Stopping server"));
- /* Calculate next timeout */
- if (sconn->retry_count >= 1) {
- sconn->retry_timeout = sconn->retry_timeout * SILC_SERVER_RETRY_MULTIPLIER;
- if (sconn->retry_timeout > SILC_SERVER_RETRY_INTERVAL_MAX)
- sconn->retry_timeout = SILC_SERVER_RETRY_INTERVAL_MAX;
- } else {
- sconn->retry_timeout = server->params->retry_interval_min;
+ if (server->schedule) {
+ silc_schedule_stop(server->schedule);
+ silc_schedule_uninit(server->schedule);
+ server->schedule = NULL;
}
- 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 > server->params->retry_count &&
- server->params->retry_keep_trying == FALSE) {
- SILC_LOG_ERROR(("Could not connect to router, giving up"));
- return;
- }
+ silc_server_protocols_unregister();
- /* Wait one before retrying */
- silc_task_register(server->timeout_queue, fd, silc_server_connect_router,
- context, sconn->retry_timeout,
- server->params->retry_interval_min_usec,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ SILC_LOG_DEBUG(("Server stopped"));
}
-/* Generic routine to use connect to a router. */
+/* 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. */
-SILC_TASK_CALLBACK(silc_server_connect_router)
-{
- SilcServerConnection sconn = (SilcServerConnection)context;
- SilcServer server = sconn->server;
+void silc_server_start_key_exchange(SilcServer server,
+ SilcServerConnection sconn,
+ int sock)
+{
SilcSocketConnection newsocket;
SilcProtocol protocol;
SilcServerKEInternalContext *proto_ctx;
- int sock;
-
- SILC_LOG_INFO(("Connecting to the router %s on port %d",
- sconn->remote_host, sconn->remote_port));
-
- /* Connect to remote host */
- sock = silc_net_create_connection(sconn->remote_port,
- sconn->remote_host);
- if (sock < 0) {
- SILC_LOG_ERROR(("Could not connect to router"));
- silc_task_register(server->timeout_queue, fd,
- silc_server_connect_to_router_retry,
- context, 0, 1, SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
- return;
- }
+ void *context;
/* Set socket options */
silc_net_set_socket_nonblock(sock);
/* Register a timeout task that will be executed if the protocol
is not executed within set limit. */
proto_ctx->timeout_task =
- silc_task_register(server->timeout_queue, sock,
+ silc_schedule_task_add(server->schedule, sock,
silc_server_timeout_remote,
server, server->params->protocol_timeout,
server->params->protocol_timeout_usec,
SILC_REGISTER_CONNECTION_FOR_IO(sock);
/* Run the protocol */
- silc_protocol_execute(protocol, server->timeout_queue, 0, 0);
+ silc_protocol_execute(protocol, server->schedule, 0, 0);
+}
+
+/* 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 = (SilcServerConnection)context;
+ SilcServer server = sconn->server;
+
+ SILC_LOG_INFO(("Retrying connecting to a router"));
+
+ /* Calculate next timeout */
+ if (sconn->retry_count >= 1) {
+ sconn->retry_timeout = sconn->retry_timeout * SILC_SERVER_RETRY_MULTIPLIER;
+ if (sconn->retry_timeout > SILC_SERVER_RETRY_INTERVAL_MAX)
+ sconn->retry_timeout = SILC_SERVER_RETRY_INTERVAL_MAX;
+ } else {
+ sconn->retry_timeout = server->params->retry_interval_min;
+ }
+ 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 > server->params->retry_count &&
+ server->params->retry_keep_trying == FALSE) {
+ SILC_LOG_ERROR(("Could not connect to router, giving up"));
+ silc_free(sconn->remote_host);
+ silc_free(sconn);
+ return;
+ }
+
+ /* Wait one before retrying */
+ silc_schedule_task_add(server->schedule, fd, silc_server_connect_router,
+ context, sconn->retry_timeout,
+ server->params->retry_interval_min_usec,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+}
+
+/* Generic routine to use connect to a router. */
+
+SILC_TASK_CALLBACK(silc_server_connect_router)
+{
+ SilcServerConnection sconn = (SilcServerConnection)context;
+ SilcServer server = sconn->server;
+ int sock;
+
+ SILC_LOG_INFO(("Connecting to the %s %s on port %d",
+ (sconn->backup ? "backup router" : "router"),
+ sconn->remote_host, sconn->remote_port));
+
+ server->router_connect = time(0);
+
+ /* Connect to remote host */
+ sock = silc_net_create_connection(server->config->listen_port->local_ip,
+ sconn->remote_port,
+ sconn->remote_host);
+ if (sock < 0) {
+ SILC_LOG_ERROR(("Could not connect to router"));
+ silc_schedule_task_add(server->schedule, fd,
+ silc_server_connect_to_router_retry,
+ context, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ return;
+ }
+
+ /* Continue with key exchange protocol */
+ silc_server_start_key_exchange(server, sconn, sock);
}
/* This function connects to our primary router or if we are a router this
{
SilcServer server = (SilcServer)context;
SilcServerConnection sconn;
+ SilcServerConfigSectionServerConnection *ptr;
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) {
SILC_LOG_DEBUG(("We are normal server"));
+ } else if (server->server_type == SILC_ROUTER) {
+ SILC_LOG_DEBUG(("We are router"));
+ } else {
+ SILC_LOG_DEBUG(("We are backup router/normal server"));
+ }
- /* Create connection to the router, if configured. */
- if (server->config->routers) {
+ /* Create the connections to all our routes */
+ ptr = server->config->routers;
+ while (ptr) {
+
+ SILC_LOG_DEBUG(("%s connection [%s] %s:%d",
+ ptr->backup_router ? "Backup router" : "Router",
+ ptr->initiator ? "Initiator" : "Responder",
+ ptr->host, ptr->port));
+ if (ptr->initiator) {
/* Allocate connection object for hold connection specific stuff. */
sconn = silc_calloc(1, sizeof(*sconn));
sconn->server = server;
- sconn->remote_host = strdup(server->config->routers->host);
- sconn->remote_port = server->config->routers->port;
-
- silc_task_register(server->timeout_queue, fd,
- silc_server_connect_router,
- (void *)sconn, 0, 1, SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
- return;
- }
- }
-
- /* If we are a SILC router we need to establish all of our primary
- routes. */
- if (server->server_type == SILC_ROUTER) {
- SilcServerConfigSectionServerConnection *ptr;
-
- SILC_LOG_DEBUG(("We are router"));
-
- /* Create the connections to all our routes */
- ptr = server->config->routers;
- while (ptr) {
-
- SILC_LOG_DEBUG(("Router connection [%s] %s:%d",
- ptr->initiator ? "Initiator" : "Responder",
- ptr->host, ptr->port));
-
- if (ptr->initiator) {
- /* Allocate connection object for hold connection specific stuff. */
- sconn = silc_calloc(1, sizeof(*sconn));
- sconn->server = server;
- sconn->remote_host = strdup(ptr->host);
- sconn->remote_port = ptr->port;
-
- silc_task_register(server->timeout_queue, fd,
- silc_server_connect_router,
- (void *)sconn, 0, 1, SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ 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;
}
- if (!ptr->next)
- return;
+ if (!server->router_conn && !sconn->backup)
+ server->router_conn = sconn;
- ptr = ptr->next;
+ silc_schedule_task_add(server->schedule, fd,
+ silc_server_connect_router,
+ (void *)sconn, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
}
+
+ if (!ptr->next)
+ return;
+
+ ptr = ptr->next;
}
SILC_LOG_DEBUG(("No router(s), server will be standalone"));
silc_packet_context_free(ctx->packet);
if (ctx->ske)
silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
+ silc_free(ctx->dest_id);
silc_free(ctx);
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_failure_callback);
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_failure_callback);
silc_server_disconnect_remote(server, sock, "Server closed connection: "
"Key exchange failed");
return;
/* We now have the key material as the result of the key exchange
protocol. Take the key material into use. Free the raw key material
as soon as we've set them into use. */
- if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
+ if (!silc_server_protocol_ke_set_keys(server, ctx->ske,
+ ctx->sock, ctx->keymat,
ctx->ske->prop->cipher,
ctx->ske->prop->pkcs,
ctx->ske->prop->hash,
silc_packet_context_free(ctx->packet);
if (ctx->ske)
silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
+ silc_free(ctx->dest_id);
silc_free(ctx);
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_failure_callback);
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_failure_callback);
silc_server_disconnect_remote(server, sock, "Server closed connection: "
"Key exchange failed");
return;
silc_packet_context_free(ctx->packet);
if (ctx->ske)
silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
+ silc_free(ctx->dest_id);
silc_free(ctx);
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_failure_callback);
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_failure_callback);
silc_server_disconnect_remote(server, sock, "Server closed connection: "
"Key exchange failed");
return;
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_schedule_task_add(server->schedule, sock->sock,
silc_server_timeout_remote,
(void *)server, 15, 0,
SILC_TASK_TIMEOUT,
SILC_TASK_PRI_LOW);
/* Run the protocol */
- silc_protocol_execute(sock->protocol, server->timeout_queue, 0, 0);
+ silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
}
/* Finalizes the connection to router. Registers a server task to the
if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
+ silc_free(ctx->dest_id);
silc_server_disconnect_remote(server, sock, "Server closed connection: "
"Authentication failed");
goto out;
/* 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) {
- silc_task_register(server->io_queue, server->sock,
- silc_server_accept_new_connection,
- (void *)server, 0, 0,
- SILC_TASK_FD,
- SILC_TASK_PRI_NORMAL);
+ if (!server->listenning && !sconn->backup) {
+ silc_schedule_task_add(server->schedule, server->sock,
+ silc_server_accept_new_connection,
+ (void *)server, 0, 0,
+ SILC_TASK_FD,
+ SILC_TASK_PRI_NORMAL);
server->listenning = TRUE;
}
silc_buffer_free(packet);
silc_free(id_string);
- SILC_LOG_DEBUG(("Connected to router %s", sock->hostname));
+ SILC_LOG_INFO(("Connected to router %s", sock->hostname));
+
+ /* Check that we do not have this ID already */
+ id_entry = silc_idlist_find_server_by_id(server->local_list,
+ ctx->dest_id, TRUE, NULL);
+ if (id_entry) {
+ silc_idcache_del_by_context(server->local_list->servers, id_entry);
+ } else {
+ id_entry = silc_idlist_find_server_by_id(server->global_list,
+ ctx->dest_id, TRUE, NULL);
+ if (id_entry)
+ silc_idcache_del_by_context(server->global_list->servers, id_entry);
+ }
- /* Add the connected router to local server list */
- server->standalone = FALSE;
- id_entry = silc_idlist_add_server(server->local_list, strdup(sock->hostname),
+ SILC_LOG_DEBUG(("New server id(%s)",
+ silc_id_render(ctx->dest_id, SILC_ID_SERVER)));
+
+ /* Add the connected router to global server list */
+ id_entry = silc_idlist_add_server(server->global_list,
+ strdup(sock->hostname),
SILC_ROUTER, ctx->dest_id, NULL, sock);
if (!id_entry) {
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
+ silc_free(ctx->dest_id);
silc_server_disconnect_remote(server, sock, "Server closed connection: "
"Authentication failed");
goto out;
silc_free(sock->user_data);
sock->user_data = (void *)id_entry;
sock->type = SILC_SOCKET_TYPE_ROUTER;
- server->id_entry->router = id_entry;
- server->router = id_entry;
idata = (SilcIDListData)sock->user_data;
- idata->registered = TRUE;
+ idata->status |= SILC_IDLIST_STATUS_REGISTERED;
/* Perform keepalive. The `hb_context' will be freed automatically
when finally calling the silc_socket_free function. XXX hardcoded
hb_context->server = server;
silc_socket_set_heartbeat(sock, 600, hb_context,
silc_server_perform_heartbeat,
- server->timeout_queue);
+ server->schedule);
/* Register re-key timeout */
idata->rekey->timeout = 3600; /* XXX hardcoded */
idata->rekey->context = (void *)server;
- silc_task_register(server->timeout_queue, sock->sock,
- silc_server_rekey_callback,
- (void *)sock, idata->rekey->timeout, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ silc_schedule_task_add(server->schedule, sock->sock,
+ silc_server_rekey_callback,
+ (void *)sock, idata->rekey->timeout, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
- /* If we are router then announce our possible servers. */
- if (server->server_type == SILC_ROUTER)
- silc_server_announce_servers(server);
+ if (!sconn->backup) {
+ /* Mark this router our primary router if we're still standalone */
+ if (server->standalone) {
+ server->id_entry->router = id_entry;
+ server->router = id_entry;
+ server->standalone = FALSE;
+
+ /* If we are router then announce our possible servers. */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_announce_servers(server, FALSE, 0,
+ server->router->connection);
+
+ /* Announce our clients and channels to the router */
+ silc_server_announce_clients(server, 0, server->router->connection);
+ silc_server_announce_channels(server, 0, server->router->connection);
+ }
+ } else {
+ /* Add this server to be our backup router */
+ silc_server_backup_add(server, id_entry, sconn->backup_replace_ip,
+ sconn->backup_replace_port, FALSE);
+ }
+
+ sock->protocol = NULL;
- /* Announce our clients and channels to the router */
- silc_server_announce_clients(server);
- silc_server_announce_channels(server);
+ /* Call the completion callback to indicate that we've connected to
+ the router */
+ if (sconn->callback)
+ (*sconn->callback)(server, id_entry, sconn->callback_context);
out:
/* Free the temporary connection data context */
if (sconn) {
silc_free(sconn->remote_host);
+ silc_free(sconn->backup_replace_ip);
silc_free(sconn);
}
+ if (sconn == server->router_conn)
+ server->router_conn = NULL;
/* Free the protocol object */
+ if (sock->protocol == protocol)
+ sock->protocol = NULL;
silc_protocol_free(protocol);
if (ctx->packet)
silc_packet_context_free(ctx->packet);
if (ctx->ske)
silc_ske_free(ctx->ske);
+ silc_free(ctx->auth_data);
silc_free(ctx);
- sock->protocol = NULL;
}
-/* Accepts new connections to the server. Accepting new connections are
- done in three parts to make it async. */
+/* Host lookup callbcak that is called after the incoming connection's
+ IP and FQDN lookup is performed. This will actually check the acceptance
+ of the incoming connection and will register the key exchange protocol
+ for this connection. */
-SILC_TASK_CALLBACK(silc_server_accept_new_connection)
+static void
+silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
+ void *context)
{
SilcServer server = (SilcServer)context;
- SilcSocketConnection newsocket;
SilcServerKEInternalContext *proto_ctx;
- int sock, port;
void *cconfig, *sconfig, *rconfig;
SilcServerConfigSectionDenyConnection *deny;
+ int port;
- SILC_LOG_DEBUG(("Accepting new connection"));
-
- server->stat.conn_attempts++;
-
- sock = silc_net_accept_connection(server->sock);
- if (sock < 0) {
- SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
- server->stat.conn_failures++;
- return;
- }
+ SILC_LOG_DEBUG(("Start"));
- /* Check max connections */
- if (sock > SILC_SERVER_MAX_CONNECTIONS) {
- SILC_LOG_ERROR(("Refusing connection, server is full"));
+ /* Check whether we could resolve both IP and FQDN. */
+ if (!sock->ip || (!strcmp(sock->ip, sock->hostname) &&
+ server->params->require_reverse_mapping)) {
+ SILC_LOG_ERROR(("IP/DNS lookup failed %s",
+ sock->hostname ? sock->hostname :
+ sock->ip ? sock->ip : ""));
server->stat.conn_failures++;
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: Unknown host");
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 name and address lookups for the remote host. */
- if (!silc_net_check_host_by_sock(sock, &newsocket->hostname,
- &newsocket->ip)) {
- if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
- !newsocket->ip) {
- SILC_LOG_ERROR(("IP/DNS lookup failed %s",
- newsocket->hostname ? newsocket->hostname :
- newsocket->ip ? newsocket->ip : ""));
- server->stat.conn_failures++;
- return;
- }
- if (!newsocket->hostname)
- newsocket->hostname = strdup(newsocket->ip);
- }
- newsocket->port = silc_net_get_remote_port(sock);
-
/* 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);
+ SILC_REGISTER_CONNECTION_FOR_IO(sock->sock);
- SILC_LOG_INFO(("Incoming connection from %s (%s)", newsocket->hostname,
- newsocket->ip));
+ SILC_LOG_INFO(("Incoming connection from %s (%s)", sock->hostname,
+ sock->ip));
- port = server->sockets[fd]->port; /* Listenning port */
+ port = server->sockets[server->sock]->port; /* Listenning port */
/* Check whether this connection is denied to connect to us. */
- deny = silc_server_config_denied_conn(server->config, newsocket->ip, port);
+ deny = silc_server_config_denied_conn(server->config, sock->ip, port);
if (!deny)
- deny = silc_server_config_denied_conn(server->config, newsocket->hostname,
+ deny = silc_server_config_denied_conn(server->config, sock->hostname,
port);
if (deny) {
/* The connection is denied */
SILC_LOG_INFO(("Connection %s (%s) is denied",
- newsocket->hostname, newsocket->ip));
- silc_server_disconnect_remote(server, newsocket, deny->comment ?
+ sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock, deny->comment ?
deny->comment :
"Server closed connection: "
"Connection refused");
have to check all configurations since we don't know what type of
connection this is. */
if (!(cconfig = silc_server_config_find_client_conn(server->config,
- newsocket->ip, port)))
+ sock->ip, port)))
cconfig = silc_server_config_find_client_conn(server->config,
- newsocket->hostname,
+ sock->hostname,
port);
if (!(sconfig = silc_server_config_find_server_conn(server->config,
- newsocket->ip,
+ sock->ip,
port)))
sconfig = silc_server_config_find_server_conn(server->config,
- newsocket->hostname,
+ sock->hostname,
port);
if (!(rconfig = silc_server_config_find_router_conn(server->config,
- newsocket->ip, port)))
+ sock->ip, port)))
rconfig = silc_server_config_find_router_conn(server->config,
- newsocket->hostname,
+ sock->hostname,
port);
if (!cconfig && !sconfig && !rconfig) {
- silc_server_disconnect_remote(server, newsocket,
+ SILC_LOG_INFO(("Connection %s (%s) is not allowed",
+ sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
"Server closed connection: "
"Connection refused");
server->stat.conn_failures++;
sent as context for the protocol. */
proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
proto_ctx->server = context;
- proto_ctx->sock = newsocket;
+ proto_ctx->sock = sock;
proto_ctx->rng = server->rng;
proto_ctx->responder = TRUE;
proto_ctx->cconfig = cconfig;
there before we start the protocol. */
server->stat.auth_attempts++;
silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE,
- &newsocket->protocol, proto_ctx,
+ &sock->protocol, proto_ctx,
silc_server_accept_new_connection_second);
/* Register a timeout task that will be executed if the connector
now, this is a hard coded limit. After 60 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, 60, 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_LOW);
+ silc_schedule_task_add(server->schedule, sock->sock,
+ silc_server_timeout_remote,
+ context, 60, 0,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_LOW);
+}
+
+/* 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;
+ int sock;
+
+ SILC_LOG_DEBUG(("Accepting new connection"));
+
+ server->stat.conn_attempts++;
+
+ sock = silc_net_accept_connection(server->sock);
+ if (sock < 0) {
+ SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
+ server->stat.conn_failures++;
+ return;
+ }
+
+ /* Check max connections */
+ if (sock > SILC_SERVER_MAX_CONNECTIONS) {
+ SILC_LOG_ERROR(("Refusing connection, server is full"));
+ server->stat.conn_failures++;
+ 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;
+
+ /* Perform asynchronous host lookup. This will lookup the IP and the
+ FQDN of the remote connection. After the lookup is done the connection
+ is accepted further. */
+ silc_socket_host_lookup(newsocket, TRUE,
+ silc_server_accept_new_connection_lookup, context,
+ server->schedule);
}
/* Second part of accepting new connection. Key exchange protocol has been
silc_packet_context_free(ctx->packet);
if (ctx->ske)
silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
+ silc_free(ctx->dest_id);
silc_free(ctx);
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_failure_callback);
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_failure_callback);
silc_server_disconnect_remote(server, sock, "Server closed connection: "
"Key exchange failed");
server->stat.auth_failures++;
/* We now have the key material as the result of the key exchange
protocol. Take the key material into use. Free the raw key material
as soon as we've set them into use. */
- if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
+ if (!silc_server_protocol_ke_set_keys(server, ctx->ske,
+ ctx->sock, ctx->keymat,
ctx->ske->prop->cipher,
ctx->ske->prop->pkcs,
ctx->ske->prop->hash,
silc_packet_context_free(ctx->packet);
if (ctx->ske)
silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
+ silc_free(ctx->dest_id);
silc_free(ctx);
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_failure_callback);
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_failure_callback);
silc_server_disconnect_remote(server, sock, "Server closed connection: "
"Key exchange failed");
server->stat.auth_failures++;
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);
+ silc_schedule_task_add(server->schedule, 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
SilcServer server = (SilcServer)ctx->server;
SilcSocketConnection sock = ctx->sock;
SilcServerHBContext hb_context;
+ SilcUnknownEntry entry = (SilcUnknownEntry)sock->user_data;
void *id_entry = NULL;
SILC_LOG_DEBUG(("Start"));
silc_packet_context_free(ctx->packet);
if (ctx->ske)
silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
+ silc_free(ctx->dest_id);
silc_free(ctx);
- if (sock)
- sock->protocol = NULL;
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_failure_callback);
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_failure_callback);
silc_server_disconnect_remote(server, sock, "Server closed connection: "
"Authentication failed");
server->stat.auth_failures++;
return;
}
- switch(ctx->conn_type) {
+ entry->data.last_receive = time(NULL);
+
+ switch (ctx->conn_type) {
case SILC_SOCKET_TYPE_CLIENT:
{
SilcClientEntry client;
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);
+ NULL, NULL, NULL, NULL, NULL, sock, 0);
if (!client) {
SILC_LOG_ERROR(("Could not add new client to cache"));
silc_free(sock->user_data);
SILC_LOG_DEBUG(("Remote host is %s",
ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
- "server" : "router"));
+ "server" : (conn->backup_router ?
+ "backup router" : "router")));
SILC_LOG_INFO(("Connection from %s (%s) is %s", sock->hostname,
sock->ip, ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
- "server" : "router"));
+ "server" : (conn->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(server->local_list, NULL,
- ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
- SILC_SERVER : SILC_ROUTER, NULL,
- ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
- server->id_entry : NULL, sock);
+ silc_idlist_add_server((ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
+ server->local_list : (conn->backup_router ?
+ server->local_list :
+ server->global_list)),
+ NULL,
+ (ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
+ SILC_SERVER : SILC_ROUTER),
+ NULL,
+ (ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
+ server->id_entry : (conn->backup_router ?
+ server->id_entry : NULL)),
+ sock);
if (!new_server) {
SILC_LOG_ERROR(("Could not add new server to cache"));
silc_free(sock->user_data);
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 (ctx->conn_type == SILC_SOCKET_TYPE_ROUTER && conn->backup_router) {
+ silc_server_backup_add(server, new_server, conn->backup_replace_ip,
+ conn->backup_replace_port, conn->backup_local);
+
+ /* Change it back to SERVER type since that's what it really is. */
+ if (conn->backup_local)
+ ctx->conn_type = SILC_SOCKET_TYPE_SERVER;
+
+ new_server->server_type = SILC_BACKUP_ROUTER;
+ }
+
/* Check whether this connection is to be our primary router connection
- if we dont' already have the primary route. */
+ if we do not already have the primary route. */
if (server->standalone && ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) {
if (silc_server_config_is_primary_route(server->config) &&
!conn->initiator)
hb_context->server = server;
silc_socket_set_heartbeat(sock, 600, hb_context,
silc_server_perform_heartbeat,
- server->timeout_queue);
+ server->schedule);
out:
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_failure_callback);
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_failure_callback);
silc_protocol_free(protocol);
if (ctx->packet)
silc_packet_context_free(ctx->packet);
if (ctx->ske)
silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
+ silc_free(ctx->dest_id);
silc_free(ctx);
sock->protocol = NULL;
}
SilcIDListData idata;
SilcCipher cipher = NULL;
SilcHmac hmac = NULL;
+ uint32 sequence = 0;
int ret;
if (!sock)
if (ret == -1)
SILC_LOG_ERROR(("Error receiving packet from connection "
- "%s:%d [%s]", sock->hostname, sock->port,
+ "%s:%d [%s] %s", sock->hostname, sock->port,
(sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
- "Router")));
+ "Router"), strerror(errno)));
return;
}
close the connection */
if (SILC_IS_DISCONNECTING(sock)) {
if (sock->user_data)
- silc_server_free_sock_user_data(server, sock);
+ silc_server_free_sock_user_data(server, sock, NULL);
silc_server_close_connection(server, sock);
return;
}
SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
SILC_SET_DISCONNECTING(sock);
- /* If the closed connection was our primary router connection the
- start re-connecting phase. */
- if (!server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER &&
- sock == server->router->connection)
- silc_task_register(server->timeout_queue, 0,
- silc_server_connect_to_router,
- context, 1, 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ if (sock->user_data) {
+ char tmp[128];
+ if (silc_socket_get_error(sock, tmp, sizeof(tmp) - 1))
+ silc_server_free_sock_user_data(server, sock, tmp);
+ else
+ silc_server_free_sock_user_data(server, sock, NULL);
+ } else if (server->router_conn && server->router_conn->sock == sock &&
+ !server->router && server->standalone)
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_connect_to_router,
+ server, 1, 0,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
- if (sock->user_data)
- silc_server_free_sock_user_data(server, sock);
silc_server_close_connection(server, sock);
return;
}
/* Get keys and stuff from ID entry */
idata = (SilcIDListData)sock->user_data;
if (idata) {
- idata->last_receive = time(NULL);
cipher = idata->receive_key;
hmac = idata->hmac_receive;
+ sequence = idata->psn_receive;
}
/* Process the packet. This will call the parser that will then
decrypt and parse the packet. */
- silc_packet_receive_process(sock, cipher, hmac, silc_server_packet_parse,
- server);
-}
-
-/* Callback function that the silc_packet_decrypt will call to make the
- decision whether the packet is normal or special packet. We will
- return TRUE if it is normal and FALSE if it is special */
-
-static int silc_server_packet_decrypt_check(SilcPacketType packet_type,
- SilcBuffer buffer,
- SilcPacketContext *packet,
- void *context)
-{
- SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
- SilcServer server = (SilcServer)parse_ctx->context;
-
- /* Packet is normal packet, if:
-
- 1) packet is private message packet and does not have private key set
- 2) is other packet than channel message packet
- 3) is channel message packet and remote is router and we are router
-
- all other packets are special packets
- */
-
- if (packet_type == SILC_PACKET_PRIVATE_MESSAGE &&
- (buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
- return FALSE;
-
- if (packet_type != SILC_PACKET_CHANNEL_MESSAGE ||
- (packet_type == SILC_PACKET_CHANNEL_MESSAGE &&
- parse_ctx->sock->type == SILC_SOCKET_TYPE_ROUTER &&
- server->server_type == SILC_ROUTER))
- return TRUE;
+ ret = silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ?
+ TRUE : FALSE, cipher, hmac, sequence,
+ silc_server_packet_parse, server);
+
+ /* If this socket connection is not authenticated yet and the packet
+ processing failed we will drop the connection since it can be
+ a malicious flooder. */
+ if (sock->type == SILC_SOCKET_TYPE_UNKNOWN && ret == FALSE &&
+ (!sock->protocol || sock->protocol->protocol->type ==
+ SILC_PROTOCOL_SERVER_KEY_EXCHANGE)) {
+ SILC_LOG_DEBUG(("Bad data sent from unknown connection %d", sock->sock));
+ SILC_SET_DISCONNECTING(sock);
- return FALSE;
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_server_close_connection(server, sock);
+ }
}
/* Parses whole packet, received earlier. */
SILC_LOG_DEBUG(("Start"));
- /* Decrypt the received packet */
- ret = silc_packet_decrypt(idata ? idata->receive_key : NULL,
- idata ? idata->hmac_receive : NULL,
- packet->buffer, packet,
- silc_server_packet_decrypt_check, parse_ctx);
- if (ret < 0) {
- SILC_LOG_WARNING(("Packet decryption failed for connection "
- "%s:%d [%s]", sock->hostname, sock->port,
- (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
- sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
- sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
- "Router")));
- goto out;
- }
+ /* Parse the packet */
+ if (parse_ctx->normal)
+ ret = silc_packet_parse(packet, idata ? idata->receive_key : NULL);
+ else
+ ret = silc_packet_parse_special(packet, idata ? idata->receive_key : NULL);
- if (ret == 0) {
- /* Parse the packet. Packet type is returned. */
- ret = silc_packet_parse(packet);
- } else {
- /* Parse the packet header in special way as this is "special"
- packet type. */
- ret = silc_packet_parse_special(packet);
+ /* If entry is disabled ignore what we got. */
+ if (ret != SILC_PACKET_RESUME_ROUTER &&
+ idata && idata->status & SILC_IDLIST_STATUS_DISABLED) {
+ SILC_LOG_DEBUG(("Connection is disabled"));
+ goto out;
}
if (ret == SILC_PACKET_NONE)
if (!(packet->flags & SILC_PACKET_FLAG_BROADCAST) &&
packet->dst_id_type == SILC_ID_SERVER &&
sock->type != SILC_SOCKET_TYPE_CLIENT &&
- memcmp(packet->dst_id, server->id_string, packet->dst_id_len)) {
+ memcmp(packet->dst_id, server->id_string, server->id_string_len)) {
/* Route the packet to fastest route for the destination ID */
void *id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
packet->flags & SILC_PACKET_FLAG_BROADCAST &&
!server->standalone) {
+ /* Broadcast to our primary route */
silc_server_packet_broadcast(server, server->router->connection, packet);
+
+ /* If we have backup routers then we need to feed all broadcast
+ data to those servers. */
+ silc_server_backup_broadcast(server, sock, packet);
}
}
out:
- /* silc_buffer_clear(sock->inbuf); */
silc_packet_context_free(packet);
silc_free(parse_ctx);
}
/* Parser callback called by silc_packet_receive_process. This merely
registers timeout that will handle the actual parsing when appropriate. */
-void silc_server_packet_parse(SilcPacketParserContext *parser_context)
+bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
+ void *context)
{
- SilcServer server = (SilcServer)parser_context->context;
+ SilcServer server = (SilcServer)context;
SilcSocketConnection sock = parser_context->sock;
+ SilcIDListData idata = (SilcIDListData)sock->user_data;
+
+ if (idata)
+ idata->psn_receive = parser_context->packet->sequence + 1;
+
+ /* If protocol for this connection is key exchange or rekey then we'll
+ process all packets synchronously, since there might be packets in
+ queue that we are not able to decrypt without first processing the
+ packets before them. */
+ if ((parser_context->packet->type == SILC_PACKET_REKEY ||
+ parser_context->packet->type == SILC_PACKET_REKEY_DONE) ||
+ (sock->protocol && sock->protocol->protocol &&
+ (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
+ sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY))) {
+ silc_server_packet_parse_real(server->schedule, 0, sock->sock,
+ parser_context);
+
+ /* Reprocess data since we'll return FALSE here. This is because
+ the idata->receive_key might have become valid in the last packet
+ and we want to call this processor with valid cipher. */
+ if (idata)
+ silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ?
+ TRUE : FALSE, idata->receive_key,
+ idata->hmac_receive, idata->psn_receive,
+ silc_server_packet_parse, server);
+ else
+ silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ?
+ TRUE : FALSE, NULL, NULL, 0,
+ silc_server_packet_parse, server);
+ return FALSE;
+ }
switch (sock->type) {
case SILC_SOCKET_TYPE_UNKNOWN:
case SILC_SOCKET_TYPE_CLIENT:
/* Parse the packet with timeout */
- silc_task_register(server->timeout_queue, sock->sock,
- silc_server_packet_parse_real,
- (void *)parser_context, 0, 100000,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ silc_schedule_task_add(server->schedule, sock->sock,
+ silc_server_packet_parse_real,
+ (void *)parser_context, 0, 100000,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
break;
case SILC_SOCKET_TYPE_SERVER:
case SILC_SOCKET_TYPE_ROUTER:
- /* Packets from servers are parsed as soon as possible */
- silc_task_register(server->timeout_queue, sock->sock,
- silc_server_packet_parse_real,
- (void *)parser_context, 0, 1,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ /* Packets from servers are parsed immediately */
+ silc_server_packet_parse_real(server->schedule, 0, sock->sock,
+ parser_context);
break;
default:
- return;
+ return TRUE;
}
+
+ return TRUE;
}
/* Parses the packet type and calls what ever routines the packet type
SilcPacketContext *packet)
{
SilcPacketType type = packet->type;
+ SilcIDListData idata = (SilcIDListData)sock->user_data;
SILC_LOG_DEBUG(("Parsing packet type %d", type));
/* Parse the packet type */
- switch(type) {
+ switch (type) {
case SILC_PACKET_DISCONNECT:
SILC_LOG_DEBUG(("Disconnect packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
if (sock->protocol)
- silc_protocol_execute(sock->protocol, server->timeout_queue, 0, 0);
+ silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
break;
case SILC_PACKET_FAILURE:
f->sock = sock;
/* We will wait 5 seconds to process this failure packet */
- silc_task_register(server->timeout_queue, sock->sock,
+ silc_schedule_task_add(server->schedule, sock->sock,
silc_server_failure_callback, (void *)f, 5, 0,
SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
}
SILC_LOG_DEBUG(("Channel Message packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
+ idata->last_receive = time(NULL);
silc_server_channel_message(server, sock, packet);
break;
SILC_LOG_DEBUG(("Private Message packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
+ idata->last_receive = time(NULL);
silc_server_private_message(server, sock, packet);
break;
proto_ctx->packet = silc_packet_context_dup(packet);
/* Let the protocol handle the packet */
- silc_protocol_execute(sock->protocol, server->timeout_queue, 0, 100000);
+ silc_protocol_execute(sock->protocol, server->schedule, 0, 100000);
} else {
SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
"protocol active, packet dropped."));
proto_ctx->packet = silc_packet_context_dup(packet);
/* Let the protocol handle the packet */
- silc_protocol_execute(sock->protocol, server->timeout_queue, 0, 0);
+ silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
} else {
SilcServerKEInternalContext *proto_ctx =
(SilcServerKEInternalContext *)sock->protocol->context;
break;
/* Let the protocol handle the packet */
- silc_protocol_execute(sock->protocol, server->timeout_queue,
+ silc_protocol_execute(sock->protocol, server->schedule,
0, 100000);
}
} else {
proto_ctx->packet = silc_packet_context_dup(packet);
/* Let the protocol handle the packet */
- silc_protocol_execute(sock->protocol, server->timeout_queue, 0, 0);
+ silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
} else {
SilcServerKEInternalContext *proto_ctx =
(SilcServerKEInternalContext *)sock->protocol->context;
break;
/* Let the protocol handle the packet */
- silc_protocol_execute(sock->protocol, server->timeout_queue,
+ silc_protocol_execute(sock->protocol, server->schedule,
0, 100000);
}
} else {
proto_ctx->packet = silc_packet_context_dup(packet);
/* Let the protocol handle the packet */
- silc_protocol_execute(sock->protocol, server->timeout_queue, 0, 0);
+ silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
} else {
SILC_LOG_ERROR(("Received Connection Auth packet but no authentication "
"protocol active, packet dropped."));
proto_ctx->packet = silc_packet_context_dup(packet);
/* Let the protocol handle the packet */
- silc_protocol_execute(sock->protocol, server->timeout_queue, 0, 0);
+ silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
} else {
SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
"protocol active, packet dropped."));
}
break;
+ case SILC_PACKET_FTP:
+ /* FTP packet */
+ SILC_LOG_DEBUG(("FTP packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_ftp(server, sock, packet);
+ break;
+
+ case SILC_PACKET_RESUME_ROUTER:
+ /* Resume router packet received. This packet is received for backup
+ router resuming protocol. */
+ SILC_LOG_DEBUG(("Resume router packet"));
+ 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;
sconn->remote_host = strdup(remote_host);
sconn->remote_port = port;
- silc_task_register(server->timeout_queue, 0,
- silc_server_connect_router,
- (void *)sconn, 0, 1, SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_connect_router,
+ (void *)sconn, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
}
SILC_TASK_CALLBACK(silc_server_close_connection_final)
void silc_server_close_connection(SilcServer server,
SilcSocketConnection sock)
{
- SILC_LOG_INFO(("Closing connection %s:%d [%s] (%d)", sock->hostname,
- sock->port,
- (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
- sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
- sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
- "Router"), sock->sock));
+ if (!server->sockets[sock->sock])
+ return;
- /* If any protocol is active cancel its execution */
- if (sock->protocol) {
- silc_protocol_cancel(sock->protocol, server->timeout_queue);
- sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
- silc_protocol_execute_final(sock->protocol, server->timeout_queue);
- sock->protocol = NULL;
- }
+ SILC_LOG_INFO(("Closing connection %s:%d [%s]", sock->hostname,
+ sock->port,
+ (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+ sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+ sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+ "Router")));
/* We won't listen for this connection anymore */
silc_schedule_unset_listen_fd(server->schedule, 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);
+ silc_schedule_task_del_by_fd(server->schedule, sock->sock);
/* Close the actual connection */
silc_net_close_connection(sock->sock);
server->sockets[sock->sock] = NULL;
- silc_task_register(server->timeout_queue, 0,
- silc_server_close_connection_final,
- (void *)sock, 0, 1, SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ /* If sock->user_data is NULL then we'll check for active protocols
+ here since the silc_server_free_sock_user_data has not been called
+ for this connection. */
+ if (!sock->user_data) {
+ /* If any protocol is active cancel its execution. It will call
+ the final callback which will finalize the disconnection. */
+ if (sock->protocol) {
+ silc_protocol_cancel(sock->protocol, server->schedule);
+ sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ silc_protocol_execute_final(sock->protocol, server->schedule);
+ sock->protocol = NULL;
+ return;
+ }
+ }
+
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_close_connection_final,
+ (void *)sock, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
}
/* Sends disconnect message to remote connection and disconnects the
memset(buf, 0, sizeof(buf));
va_start(ap, fmt);
- vsprintf(buf, fmt, ap);
- va_end(ap);
-
- SILC_LOG_DEBUG(("Disconnecting remote host"));
-
- SILC_LOG_INFO(("Disconnecting %s:%d [%s]", sock->hostname,
- sock->port,
- (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
- sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
- sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
- "Router")));
+ 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. */
SilcSocketConnection sock,
SilcClientEntry client,
int notify,
- char *signoff)
+ const char *signoff)
{
FreeClientInternal i = silc_calloc(1, sizeof(*i));
/* If there is pending outgoing data for the client then purge it
to the network before removing the client entry. */
- if (sock && SILC_IS_OUTBUF_PENDING(sock) &&
- (SILC_IS_DISCONNECTED(sock) == FALSE)) {
- server->stat.packets_sent++;
-
- if (sock->outbuf->data - sock->outbuf->head)
- silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+ silc_server_packet_queue_purge(server, sock);
- silc_packet_send(sock, TRUE);
-
- SILC_SET_CONNECTION_FOR_INPUT(server->schedule, sock->sock);
- SILC_UNSET_OUTBUF_PENDING(sock);
- silc_buffer_clear(sock->outbuf);
- }
+ if (!client->id)
+ return;
/* Send SIGNOFF notify to routers. */
if (notify && !server->standalone && server->router)
silc_server_send_notify_signoff(server, server->router->connection,
server->server_type == SILC_SERVER ?
FALSE : TRUE, client->id, signoff);
-
+
/* Remove client from all channels */
if (notify)
silc_server_remove_from_channels(server, NULL, client,
- TRUE, signoff, TRUE);
+ TRUE, (char *)signoff, TRUE);
else
silc_server_remove_from_channels(server, NULL, client,
FALSE, NULL, FALSE);
-
+
/* We will not delete the client entry right away. We will take it
into history (for WHOWAS command) for 5 minutes */
i->server = server;
i->client = client;
- silc_task_register(server->timeout_queue, 0,
- silc_server_free_client_data_timeout,
- (void *)i, 300, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
- client->data.registered = FALSE;
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_free_client_data_timeout,
+ (void *)i, 300, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+ client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+ client->router = NULL;
+ client->connection = NULL;
/* Free the client entry and everything in it */
server->stat.my_clients--;
entities. */
void silc_server_free_sock_user_data(SilcServer server,
- SilcSocketConnection sock)
+ SilcSocketConnection sock,
+ const char *signoff_message)
{
SILC_LOG_DEBUG(("Start"));
- switch(sock->type) {
+ switch (sock->type) {
case SILC_SOCKET_TYPE_CLIENT:
{
SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
- silc_server_free_client_data(server, sock, user_data, TRUE, NULL);
+ silc_server_free_client_data(server, sock, user_data, TRUE,
+ signoff_message);
break;
}
case SILC_SOCKET_TYPE_SERVER:
case SILC_SOCKET_TYPE_ROUTER:
{
SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
+ SilcServerEntry backup_router = NULL;
- /* Free all client entries that this server owns as they will
- become invalid now as well. */
if (user_data->id)
- silc_server_remove_clients_by_server(server, user_data, TRUE);
+ backup_router = silc_server_backup_get(server, user_data->id);
/* If this was our primary router connection then we're lost to
the outside world. */
if (server->router == user_data) {
- server->id_entry->router = NULL;
- server->router = NULL;
- server->standalone = TRUE;
+ /* Check whether we have a backup router connection */
+ if (!backup_router || backup_router == user_data) {
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_connect_to_router,
+ server, 1, 0,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+
+ server->id_entry->router = NULL;
+ server->router = NULL;
+ server->standalone = TRUE;
+ backup_router = NULL;
+ } else {
+ SILC_LOG_INFO(("New primary router is backup router %s",
+ backup_router->server_name));
+ SILC_LOG_DEBUG(("New primary router is backup router %s",
+ backup_router->server_name));
+ server->id_entry->router = backup_router;
+ server->router = backup_router;
+ server->router_connect = time(0);
+ server->backup_primary = TRUE;
+ if (server->server_type == SILC_BACKUP_ROUTER) {
+ server->server_type = SILC_ROUTER;
+
+ /* We'll need to constantly try to reconnect to the primary
+ router so that we'll see when it comes back online. */
+ silc_server_backup_reconnect(server, sock->ip, sock->port,
+ silc_server_backup_connected,
+ NULL);
+ }
+
+ /* Mark this connection as replaced */
+ silc_server_backup_replaced_add(server, user_data->id,
+ backup_router);
+ }
+ } else if (backup_router) {
+ SILC_LOG_INFO(("Enabling the use of backup router %s",
+ backup_router->server_name));
+ SILC_LOG_DEBUG(("Enabling the use of backup router %s",
+ backup_router->server_name));
+
+ /* Mark this connection as replaced */
+ silc_server_backup_replaced_add(server, user_data->id,
+ backup_router);
+ }
+
+ if (!backup_router) {
+ /* Free all client entries that this server owns as they will
+ become invalid now as well. */
+ if (user_data->id)
+ silc_server_remove_clients_by_server(server, user_data, TRUE);
+ if (server->server_type == SILC_SERVER)
+ silc_server_remove_channels_by_server(server, user_data);
+ } else {
+ /* Update the client entries of this server to the new backup
+ router. This also removes the clients that *really* was owned
+ by the primary router and went down with the router. */
+ silc_server_update_clients_by_server(server, user_data, backup_router,
+ TRUE, TRUE);
+ silc_server_update_servers_by_server(server, user_data, backup_router);
+ if (server->server_type == SILC_SERVER)
+ silc_server_update_channels_by_server(server, user_data,
+ backup_router);
}
/* Free the server entry */
+ silc_server_backup_del(server, user_data);
+ silc_server_backup_replaced_del(server, user_data);
silc_idlist_del_data(user_data);
- silc_idlist_del_server(server->local_list, user_data);
+ if (!silc_idlist_del_server(server->local_list, user_data))
+ silc_idlist_del_server(server->global_list, user_data);
server->stat.my_servers--;
server->stat.servers--;
if (server->server_type == SILC_ROUTER)
server->stat.cell_servers--;
+
+ if (backup_router) {
+ /* Announce all of our stuff that was created about 5 minutes ago.
+ The backup router knows all the other stuff already. */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_announce_servers(server, FALSE, time(0) - 300,
+ backup_router->connection);
+
+ /* Announce our clients and channels to the router */
+ silc_server_announce_clients(server, time(0) - 300,
+ backup_router->connection);
+ silc_server_announce_channels(server, time(0) - 300,
+ backup_router->connection);
+ }
break;
}
default:
}
}
- sock->user_data = NULL;
-}
-
-/* This function is used to remove all client entries by the server `entry'.
- This is called when the connection is lost to the server. In this case
- we must invalidate all the client entries owned by the server `entry'.
- If the `server_signoff' is TRUE then the SERVER_SIGNOFF notify is
- distributed to our local clients. */
-
-int silc_server_remove_clients_by_server(SilcServer server,
- SilcServerEntry entry,
- int server_signoff)
-{
- SilcIDCacheList list = NULL;
- SilcIDCacheEntry id_cache = NULL;
- SilcClientEntry client = NULL;
- SilcBuffer idp;
- SilcClientEntry *clients = NULL;
- uint32 clients_c = 0;
- unsigned char **argv = NULL;
- uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
- SilcHashTable servers;
- SilcChannelClientEntry chl;
- SilcChannelEntry channel;
- SilcHashTableList htl;
- int i;
-
- SILC_LOG_DEBUG(("Start"));
-
- /* Allocate the hash table that holds the channels that require
- channel key re-generation after we've removed this servers clients
- from the channels. */
- servers = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL, NULL,
- NULL, NULL, TRUE);
-
- if (server_signoff) {
- idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
- argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
- argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
- argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
- argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
- memcpy(argv[argc], idp->data, idp->len);
- argv_lens[argc] = idp->len;
- argv_types[argc] = argc + 1;
- argc++;
- silc_buffer_free(idp);
- }
-
- if (silc_idcache_get_all(server->local_list->clients, &list)) {
-
- if (silc_idcache_list_first(list, &id_cache)) {
- while (id_cache) {
- client = (SilcClientEntry)id_cache->context;
- if (client->data.registered == FALSE) {
- if (!silc_idcache_list_next(list, &id_cache))
- break;
- else
- continue;
- }
-
- if (client->router != entry) {
- if (server_signoff && client->connection) {
- clients = silc_realloc(clients,
- sizeof(*clients) * (clients_c + 1));
- clients[clients_c] = client;
- clients_c++;
- }
-
- if (!silc_idcache_list_next(list, &id_cache))
- break;
- else
- continue;
- }
-
- if (server_signoff) {
- idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
- argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
- argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
- (argc + 1));
- argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
- (argc + 1));
- argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
- memcpy(argv[argc], idp->data, idp->len);
- argv_lens[argc] = idp->len;
- argv_types[argc] = argc + 1;
- argc++;
- silc_buffer_free(idp);
- }
-
- silc_hash_table_list(client->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chl))
- silc_hash_table_replace(servers, chl->channel, chl->channel);
-
- /* Remove the client entry */
- silc_server_remove_from_channels(server, NULL, client, FALSE,
- NULL, FALSE);
- silc_idlist_del_client(server->local_list, client);
-
- if (!silc_idcache_list_next(list, &id_cache))
- break;
- }
- }
- silc_idcache_list_free(list);
- }
-
- if (silc_idcache_get_all(server->global_list->clients, &list)) {
-
- if (silc_idcache_list_first(list, &id_cache)) {
- while (id_cache) {
- client = (SilcClientEntry)id_cache->context;
- if (client->data.registered == FALSE) {
- if (!silc_idcache_list_next(list, &id_cache))
- break;
- else
- continue;
- }
-
- if (client->router != entry) {
- if (server_signoff && client->connection) {
- clients = silc_realloc(clients,
- sizeof(*clients) * (clients_c + 1));
- clients[clients_c] = client;
- clients_c++;
- }
-
- if (!silc_idcache_list_next(list, &id_cache))
- break;
- else
- continue;
- }
-
- if (server_signoff) {
- idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
- argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
- argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
- (argc + 1));
- argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
- (argc + 1));
- argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
- memcpy(argv[argc], idp->data, idp->len);
- argv_lens[argc] = idp->len;
- argv_types[argc] = argc + 1;
- argc++;
- silc_buffer_free(idp);
- }
-
- silc_hash_table_list(client->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chl))
- silc_hash_table_replace(servers, chl->channel, chl->channel);
-
- /* Remove the client entry */
- silc_server_remove_from_channels(server, NULL, client, FALSE,
- NULL, FALSE);
- silc_idlist_del_client(server->global_list, client);
-
- if (!silc_idcache_list_next(list, &id_cache))
- break;
- }
- }
- silc_idcache_list_free(list);
- }
-
- /* Send the SERVER_SIGNOFF notify */
- if (server_signoff) {
- SilcBuffer args;
-
- /* Send SERVER_SIGNOFF notify to our primary router */
- if (!server->standalone && server->router &&
- server->router != entry) {
- args = silc_argument_payload_encode(1, argv, argv_lens,
- argv_types);
- silc_server_send_notify_args(server,
- server->router->connection,
- server->server_type == SILC_SERVER ?
- FALSE : TRUE,
- SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
- argc, args);
- silc_buffer_free(args);
- }
-
- args = silc_argument_payload_encode(argc, argv, argv_lens,
- argv_types);
- /* Send to local clients */
- for (i = 0; i < clients_c; i++) {
- silc_server_send_notify_args(server, clients[i]->connection,
- FALSE, SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
- argc, args);
- }
-
- silc_free(clients);
- silc_buffer_free(args);
- for (i = 0; i < argc; i++)
- silc_free(argv[i]);
- silc_free(argv);
- silc_free(argv_lens);
- silc_free(argv_types);
- }
-
- /* We must now re-generate the channel key for all channels that had
- this server's client(s) on the channel. As they left the channel we
- must re-generate the channel key. */
- silc_hash_table_list(servers, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&channel)) {
- silc_server_create_channel_key(server, channel, 0);
- silc_server_send_channel_key(server, NULL, channel,
- server->server_type == SILC_ROUTER ?
- FALSE : !server->standalone);
- }
- silc_hash_table_free(servers);
-
- return TRUE;
-}
-
-/* Checks whether given channel has global users. If it does this returns
- TRUE and FALSE if there is only locally connected clients on the channel. */
-
-int silc_server_channel_has_global(SilcChannelEntry channel)
-{
- SilcChannelClientEntry chl;
- SilcHashTableList htl;
-
- silc_hash_table_list(channel->user_list, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
- if (chl->client->router)
- return TRUE;
- }
-
- return FALSE;
-}
-
-/* Checks whether given channel has locally connected users. If it does this
- returns TRUE and FALSE if there is not one locally connected client. */
-
-int silc_server_channel_has_local(SilcChannelEntry channel)
-{
- SilcChannelClientEntry chl;
- SilcHashTableList htl;
-
- silc_hash_table_list(channel->user_list, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
- if (!chl->client->router)
- return TRUE;
+ /* If any protocol is active cancel its execution */
+ if (sock->protocol) {
+ silc_protocol_cancel(sock->protocol, server->schedule);
+ sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ silc_protocol_execute_final(sock->protocol, server->schedule);
+ sock->protocol = NULL;
}
- return FALSE;
+ sock->user_data = NULL;
}
/* Removes client from all channels it has joined. This is used when client
if (server->server_type == SILC_ROUTER &&
silc_hash_table_count(channel->user_list) < 2) {
if (channel->rekey)
- silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
- if (!silc_idlist_del_channel(server->local_list, channel))
- silc_idlist_del_channel(server->global_list, channel);
- server->stat.my_channels--;
+ silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+ if (silc_idlist_del_channel(server->local_list, channel))
+ server->stat.my_channels--;
+ else
+ silc_idlist_del_channel(server->global_list, channel);
continue;
}
/* Remove client from channel's client list */
silc_hash_table_del(channel->user_list, chl->client);
- silc_free(chl);
- server->stat.my_chanclients--;
/* If there is no global users on the channel anymore mark the channel
as local channel. Do not check if the removed client is local client. */
- if (server->server_type == SILC_SERVER && channel->global_users &&
+ if (server->server_type != SILC_ROUTER && channel->global_users &&
chl->client->router && !silc_server_channel_has_global(channel))
channel->global_users = FALSE;
+ silc_free(chl);
+ server->stat.my_chanclients--;
+
/* If there is not at least one local user on the channel then we don't
need the channel entry anymore, we can remove it safely. */
- if (server->server_type == SILC_SERVER &&
+ if (server->server_type != SILC_ROUTER &&
!silc_server_channel_has_local(channel)) {
/* Notify about leaving client if this channel has global users. */
if (notify && channel->global_users)
strlen(signoff_message) : 0);
if (channel->rekey)
- silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
+ silc_schedule_task_del_by_context(server->schedule, channel->rekey);
if (channel->founder_key) {
/* The founder auth data exists, do not remove the channel entry */
SilcChannelClientEntry chl2;
SilcHashTableList htl2;
- channel->id = NULL;
+ channel->disabled = TRUE;
silc_hash_table_list(channel->user_list, &htl2);
while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
}
/* Remove the channel entry */
- if (!silc_idlist_del_channel(server->local_list, channel))
- silc_idlist_del_channel(server->global_list, channel);
- server->stat.my_channels--;
+ if (silc_idlist_del_channel(server->local_list, channel))
+ server->stat.my_channels--;
+ else
+ silc_idlist_del_channel(server->global_list, channel);
continue;
}
if (keygen && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
/* Re-generate channel key */
- silc_server_create_channel_key(server, channel, 0);
+ if (!silc_server_create_channel_key(server, channel, 0))
+ return;
/* Send the channel key to the channel. The key of course is not sent
to the client who was removed from the channel. */
if (server->server_type == SILC_ROUTER &&
silc_hash_table_count(channel->user_list) < 2) {
if (channel->rekey)
- silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
- if (!silc_idlist_del_channel(server->local_list, channel))
+ silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+ if (silc_idlist_del_channel(server->local_list, channel))
+ server->stat.my_channels--;
+ else
silc_idlist_del_channel(server->global_list, channel);
silc_buffer_free(clidp);
- server->stat.my_channels--;
return FALSE;
}
/* Remove client from channel's client list */
silc_hash_table_del(channel->user_list, chl->client);
- silc_free(chl);
- server->stat.my_chanclients--;
/* If there is no global users on the channel anymore mark the channel
as local channel. Do not check if the client is local client. */
- if (server->server_type == SILC_SERVER && channel->global_users &&
+ if (server->server_type != SILC_ROUTER && channel->global_users &&
chl->client->router && !silc_server_channel_has_global(channel))
channel->global_users = FALSE;
+ silc_free(chl);
+ server->stat.my_chanclients--;
+
/* If there is not at least one local user on the channel then we don't
need the channel entry anymore, we can remove it safely. */
- if (server->server_type == SILC_SERVER &&
+ if (server->server_type != SILC_ROUTER &&
!silc_server_channel_has_local(channel)) {
/* Notify about leaving client if this channel has global users. */
if (notify && channel->global_users)
silc_buffer_free(clidp);
if (channel->rekey)
- silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
+ silc_schedule_task_del_by_context(server->schedule, channel->rekey);
if (channel->founder_key) {
/* The founder auth data exists, do not remove the channel entry */
SilcChannelClientEntry chl2;
SilcHashTableList htl2;
- channel->id = NULL;
+ channel->disabled = TRUE;
silc_hash_table_list(channel->user_list, &htl2);
while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
}
/* Remove the channel entry */
- if (!silc_idlist_del_channel(server->local_list, channel))
+ if (silc_idlist_del_channel(server->local_list, channel))
+ server->stat.my_channels--;
+ else
silc_idlist_del_channel(server->global_list, channel);
- server->stat.my_channels--;
return FALSE;
}
return TRUE;
}
-/* Returns TRUE if the given client is on the channel. FALSE if not.
- This works because we assure that the user list on the channel is
- always in up to date thus we can only check the channel list from
- `client' which is faster than checking the user list from `channel'. */
-
-int silc_server_client_on_channel(SilcClientEntry client,
- SilcChannelEntry channel)
-{
- if (!client || !channel)
- return FALSE;
-
- if (silc_hash_table_find(client->channels, channel, NULL, NULL))
- return TRUE;
-
- return FALSE;
-}
-
/* 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. */
/* If we have protocol active we must assure that we call the protocol's
final callback so that all the memory is freed. */
if (sock->protocol) {
+ silc_protocol_cancel(sock->protocol, server->schedule);
sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
- silc_protocol_execute_final(sock->protocol, server->timeout_queue);
+ silc_protocol_execute_final(sock->protocol, server->schedule);
+ sock->protocol = NULL;
return;
}
if (sock->user_data)
- silc_server_free_sock_user_data(server, sock);
+ silc_server_free_sock_user_data(server, sock, NULL);
silc_server_disconnect_remote(server, sock, "Server closed connection: "
"Connection timeout");
SILC_LOG_DEBUG(("Creating new channel"));
if (!cipher)
- cipher = "aes-256-cbc";
+ cipher = SILC_DEFAULT_CIPHER;
if (!hmac)
- hmac = "hmac-sha1-96";
+ hmac = SILC_DEFAULT_HMAC;
/* Allocate cipher */
if (!silc_cipher_alloc(cipher, &key))
channel_name = strdup(channel_name);
/* Create the channel */
- silc_id_create_channel_id(router_id, server->rng, &channel_id);
+ if (!silc_id_create_channel_id(server, router_id, server->rng,
+ &channel_id)) {
+ silc_free(channel_name);
+ silc_cipher_free(key);
+ silc_hmac_free(newhmac);
+ return NULL;
+ }
entry = silc_idlist_add_channel(server->local_list, channel_name,
SILC_CHANNEL_MODE_NONE, channel_id,
- NULL, key, newhmac);
+ NULL, key, newhmac, 0);
if (!entry) {
silc_free(channel_name);
silc_cipher_free(key);
entry->hmac_name = strdup(hmac);
/* Now create the actual key material */
- silc_server_create_channel_key(server, entry,
- silc_cipher_get_key_len(key) / 8);
+ if (!silc_server_create_channel_key(server, entry,
+ silc_cipher_get_key_len(key) / 8)) {
+ silc_free(channel_name);
+ silc_cipher_free(key);
+ silc_hmac_free(newhmac);
+ silc_free(entry->cipher);
+ silc_free(entry->hmac_name);
+ return NULL;
+ }
/* Notify other routers about the new channel. We send the packet
to our primary route. */
SILC_LOG_DEBUG(("Creating new channel"));
if (!cipher)
- cipher = "aes-256-cbc";
+ cipher = SILC_DEFAULT_CIPHER;
if (!hmac)
- hmac = "hmac-sha1-96";
+ hmac = SILC_DEFAULT_HMAC;
/* Allocate cipher */
if (!silc_cipher_alloc(cipher, &key))
/* Create the channel */
entry = silc_idlist_add_channel(server->local_list, channel_name,
SILC_CHANNEL_MODE_NONE, channel_id,
- NULL, key, newhmac);
+ NULL, key, newhmac, 0);
if (!entry) {
silc_free(channel_name);
return NULL;
}
/* Now create the actual key material */
- silc_server_create_channel_key(server, entry,
- silc_cipher_get_key_len(key) / 8);
+ if (!silc_server_create_channel_key(server, entry,
+ silc_cipher_get_key_len(key) / 8)) {
+ silc_free(channel_name);
+ return NULL;
+ }
/* Notify other routers about the new channel. We send the packet
to our primary route. */
SilcServerChannelRekey rekey = (SilcServerChannelRekey)context;
SilcServer server = (SilcServer)rekey->context;
- silc_server_create_channel_key(server, rekey->channel, rekey->key_len);
- silc_server_send_channel_key(server, NULL, rekey->channel, FALSE);
+ rekey->task = NULL;
+
+ if (!silc_server_create_channel_key(server, rekey->channel, rekey->key_len))
+ return;
- silc_task_register(server->timeout_queue, 0,
- silc_server_channel_key_rekey,
- (void *)rekey, 3600, 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ silc_server_send_channel_key(server, NULL, rekey->channel, FALSE);
}
/* Generates new channel key. This is used to create the initial channel key
but also to re-generate new key for channel. If `key_len' is provided
it is the bytes of the key length. */
-void silc_server_create_channel_key(SilcServer server,
+bool silc_server_create_channel_key(SilcServer server,
SilcChannelEntry channel,
uint32 key_len)
{
if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
SILC_LOG_DEBUG(("Channel has private keys, will not generate new key"));
- return;
+ return TRUE;
}
if (!channel->channel_key)
- if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key))
- return;
+ if (!silc_cipher_alloc(SILC_DEFAULT_CIPHER, &channel->channel_key)) {
+ channel->channel_key = NULL;
+ return FALSE;
+ }
if (key_len)
len = key_len;
/* Generate HMAC key from the channel key data and set it */
if (!channel->hmac)
- silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
- silc_hash_make(channel->hmac->hash, channel->key, len, hash);
- silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
+ silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac);
+ silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, len, hash);
+ silc_hmac_set_key(channel->hmac, hash,
+ silc_hash_len(silc_hmac_get_hash(channel->hmac)));
memset(hash, 0, sizeof(hash));
if (server->server_type == SILC_ROUTER) {
channel->rekey->context = (void *)server;
channel->rekey->channel = channel;
channel->rekey->key_len = key_len;
+ if (channel->rekey->task)
+ silc_schedule_task_del(server->schedule, channel->rekey->task);
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_channel_key_rekey);
- silc_task_register(server->timeout_queue, 0,
- silc_server_channel_key_rekey,
- (void *)channel->rekey, 3600, 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ channel->rekey->task =
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_channel_key_rekey,
+ (void *)channel->rekey, 3600, 0,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
}
+
+ return TRUE;
}
/* Saves the channel key found in the encoded `key_payload' buffer. This
SILC_LOG_DEBUG(("Start"));
/* Decode channel key payload */
- payload = silc_channel_key_payload_parse(key_payload);
+ payload = silc_channel_key_payload_parse(key_payload->data,
+ key_payload->len);
if (!payload) {
- SILC_LOG_ERROR(("Bad channel key payload, dropped"));
+ SILC_LOG_ERROR(("Bad channel key payload received, dropped"));
channel = NULL;
goto out;
}
/* Create new cipher */
if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+ channel->channel_key = NULL;
channel = NULL;
goto out;
}
/* Generate HMAC key from the channel key data and set it */
if (!channel->hmac)
- silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
- silc_hash_make(channel->hmac->hash, tmp, tmp_len, hash);
- silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
+ silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac);
+ silc_hash_make(silc_hmac_get_hash(channel->hmac), tmp, tmp_len, hash);
+ silc_hmac_set_key(channel->hmac, hash,
+ silc_hash_len(silc_hmac_get_hash(channel->hmac)));
memset(hash, 0, sizeof(hash));
memset(tmp, 0, tmp_len);
channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
channel->rekey->context = (void *)server;
channel->rekey->channel = channel;
+ if (channel->rekey->task)
+ silc_schedule_task_del(server->schedule, channel->rekey->task);
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_channel_key_rekey);
- silc_task_register(server->timeout_queue, 0,
- silc_server_channel_key_rekey,
- (void *)channel->rekey, 3600, 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ channel->rekey->task =
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_channel_key_rekey,
+ (void *)channel->rekey, 3600, 0,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
}
out:
- if (id)
- silc_free(id);
+ silc_free(id);
if (payload)
silc_channel_key_payload_free(payload);
form is dictated by the New ID payload. */
static void silc_server_announce_get_servers(SilcServer server,
+ SilcServerEntry remote,
SilcIDList id_list,
- SilcBuffer *servers)
+ SilcBuffer *servers,
+ unsigned long creation_time)
{
SilcIDCacheList list;
SilcIDCacheEntry id_cache;
while (id_cache) {
entry = (SilcServerEntry)id_cache->context;
+ /* Do not announce the one we've sending our announcements and
+ do not announce ourself. Also check the creation time if it's
+ provided. */
+ if ((entry == remote) || (entry == server->id_entry) ||
+ (creation_time && entry->data.created < creation_time)) {
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+ continue;
+ }
+
idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
*servers = silc_buffer_realloc(*servers,
}
}
+static SilcBuffer
+silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
+{
+ va_list ap;
+ SilcBuffer p;
+
+ va_start(ap, argc);
+ p = silc_notify_payload_encode(notify, argc, ap);
+ va_end(ap);
+
+ return p;
+}
+
/* This function is used by router to announce existing servers to our
- primary router when we've connected to it. */
+ primary router when we've connected to it. If `creation_time' is non-zero
+ then only the servers that has been created after the `creation_time'
+ will be announced. */
-void silc_server_announce_servers(SilcServer server)
+void silc_server_announce_servers(SilcServer server, bool global,
+ unsigned long creation_time,
+ SilcSocketConnection remote)
{
SilcBuffer servers = NULL;
SILC_LOG_DEBUG(("Announcing servers"));
/* Get servers in local list */
- silc_server_announce_get_servers(server, server->local_list, &servers);
+ silc_server_announce_get_servers(server, remote->user_data,
+ server->local_list, &servers,
+ creation_time);
- /* Get servers in global list */
- silc_server_announce_get_servers(server, server->global_list, &servers);
+ if (global)
+ /* Get servers in global list */
+ silc_server_announce_get_servers(server, remote->user_data,
+ server->global_list, &servers,
+ creation_time);
if (servers) {
silc_buffer_push(servers, servers->data - servers->head);
SILC_LOG_HEXDUMP(("servers"), servers->data, servers->len);
/* Send the packet */
- silc_server_packet_send(server, server->router->connection,
+ silc_server_packet_send(server, remote,
SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
servers->data, servers->len, TRUE);
static void silc_server_announce_get_clients(SilcServer server,
SilcIDList id_list,
- SilcBuffer *clients)
+ SilcBuffer *clients,
+ SilcBuffer *umodes,
+ unsigned long creation_time)
{
SilcIDCacheList list;
SilcIDCacheEntry id_cache;
SilcClientEntry client;
SilcBuffer idp;
+ SilcBuffer tmp;
+ unsigned char mode[4];
/* Go through all clients in the list */
if (silc_idcache_get_all(id_list->clients, &list)) {
while (id_cache) {
client = (SilcClientEntry)id_cache->context;
+ if (creation_time && client->data.created < creation_time) {
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+ continue;
+ }
+
idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
*clients = silc_buffer_realloc(*clients,
silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data));
silc_buffer_put(*clients, idp->data, idp->len);
silc_buffer_pull(*clients, idp->len);
+
+ SILC_PUT32_MSB(client->mode, mode);
+ tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_UMODE_CHANGE,
+ 2, idp->data, idp->len,
+ mode, 4);
+ *umodes = silc_buffer_realloc(*umodes,
+ (*umodes ?
+ (*umodes)->truelen + tmp->len :
+ tmp->len));
+ silc_buffer_pull_tail(*umodes, ((*umodes)->end - (*umodes)->data));
+ silc_buffer_put(*umodes, tmp->data, tmp->len);
+ silc_buffer_pull(*umodes, tmp->len);
+ silc_buffer_free(tmp);
+
silc_buffer_free(idp);
if (!silc_idcache_list_next(list, &id_cache))
}
/* This function is used to announce our existing clients to our router
- when we've connected to it. */
+ when we've connected to it. If `creation_time' is non-zero then only
+ the clients that has been created after the `creation_time' will be
+ announced. */
-void silc_server_announce_clients(SilcServer server)
+void silc_server_announce_clients(SilcServer server,
+ unsigned long creation_time,
+ SilcSocketConnection remote)
{
SilcBuffer clients = NULL;
+ SilcBuffer umodes = NULL;
SILC_LOG_DEBUG(("Announcing clients"));
/* Get clients in local list */
silc_server_announce_get_clients(server, server->local_list,
- &clients);
+ &clients, &umodes, creation_time);
/* As router we announce our global list as well */
if (server->server_type == SILC_ROUTER)
silc_server_announce_get_clients(server, server->global_list,
- &clients);
+ &clients, &umodes, creation_time);
if (clients) {
silc_buffer_push(clients, clients->data - clients->head);
SILC_LOG_HEXDUMP(("clients"), clients->data, clients->len);
/* Send the packet */
- silc_server_packet_send(server, server->router->connection,
+ silc_server_packet_send(server, remote,
SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
clients->data, clients->len, TRUE);
silc_buffer_free(clients);
}
+
+ if (umodes) {
+ silc_buffer_push(umodes, umodes->data - umodes->head);
+ SILC_LOG_HEXDUMP(("umodes"), umodes->data, umodes->len);
+
+ /* Send the packet */
+ silc_server_packet_send(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ umodes->data, umodes->len, TRUE);
+
+ silc_buffer_free(umodes);
+ }
}
-static SilcBuffer
-silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
+/* Returns channel's topic for announcing it */
+
+void silc_server_announce_get_channel_topic(SilcServer server,
+ SilcChannelEntry channel,
+ SilcBuffer *topic)
{
- va_list ap;
+ SilcBuffer chidp;
- va_start(ap, argc);
- return silc_notify_payload_encode(notify, argc, ap);
+ if (channel->topic) {
+ chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+ *topic = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_TOPIC_SET, 2,
+ chidp->data, chidp->len,
+ channel->topic,
+ strlen(channel->topic));
+ silc_buffer_free(chidp);
+ }
}
/* Returns assembled packets for channel users of the `channel'. */
SilcBuffer *channel_users,
SilcBuffer **channel_users_modes,
uint32 *channel_users_modes_c,
- SilcChannelID ***channel_ids)
+ SilcBuffer **channel_topics,
+ SilcChannelID ***channel_ids,
+ unsigned long creation_time)
{
SilcIDCacheList list;
SilcIDCacheEntry id_cache;
uint16 name_len;
int len;
int i = *channel_users_modes_c;
+ bool announce;
SILC_LOG_DEBUG(("Start"));
if (silc_idcache_list_first(list, &id_cache)) {
while (id_cache) {
channel = (SilcChannelEntry)id_cache->context;
-
+
+ if (creation_time && channel->created < creation_time)
+ announce = FALSE;
+ else
+ announce = TRUE;
+
cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
name_len = strlen(channel->channel_name);
- len = 4 + name_len + id_len + 4;
- *channels =
- silc_buffer_realloc(*channels,
- (*channels ? (*channels)->truelen + len : len));
- silc_buffer_pull_tail(*channels,
- ((*channels)->end - (*channels)->data));
- silc_buffer_format(*channels,
- SILC_STR_UI_SHORT(name_len),
- SILC_STR_UI_XNSTRING(channel->channel_name,
- name_len),
- SILC_STR_UI_SHORT(id_len),
- SILC_STR_UI_XNSTRING(cid, id_len),
- SILC_STR_UI_INT(channel->mode),
- SILC_STR_END);
- silc_buffer_pull(*channels, len);
+ if (announce) {
+ len = 4 + name_len + id_len + 4;
+ *channels =
+ silc_buffer_realloc(*channels,
+ (*channels ? (*channels)->truelen +
+ len : len));
+ silc_buffer_pull_tail(*channels,
+ ((*channels)->end - (*channels)->data));
+ silc_buffer_format(*channels,
+ SILC_STR_UI_SHORT(name_len),
+ SILC_STR_UI_XNSTRING(channel->channel_name,
+ name_len),
+ SILC_STR_UI_SHORT(id_len),
+ SILC_STR_UI_XNSTRING(cid, id_len),
+ SILC_STR_UI_INT(channel->mode),
+ SILC_STR_END);
+ silc_buffer_pull(*channels, len);
+ }
+ /* Channel user modes */
*channel_users_modes = silc_realloc(*channel_users_modes,
sizeof(**channel_users_modes) *
(i + 1));
(*channel_ids)[i] = NULL;
silc_server_announce_get_channel_users(server, channel,
channel_users,
- channel_users_modes[i]);
+ &(*channel_users_modes)[i]);
(*channel_ids)[i] = channel->id;
+
+ /* Channel's topic */
+ *channel_topics = silc_realloc(*channel_topics,
+ sizeof(**channel_topics) * (i + 1));
+ (*channel_topics)[i] = NULL;
+ silc_server_announce_get_channel_topic(server, channel,
+ &(*channel_topics)[i]);
i++;
if (!silc_idcache_list_next(list, &id_cache))
/* This function is used to announce our existing channels to our router
when we've connected to it. This also announces the users on the
- channels to the router. */
-
-void silc_server_announce_channels(SilcServer server)
+ channels to the router. If the `creation_time' is non-zero only the
+ channels that was created after the `creation_time' are announced.
+ Note that the channel users are still announced even if the `creation_time'
+ was provided. */
+
+void silc_server_announce_channels(SilcServer server,
+ unsigned long creation_time,
+ SilcSocketConnection remote)
{
SilcBuffer channels = NULL, channel_users = NULL;
SilcBuffer *channel_users_modes = NULL;
+ SilcBuffer *channel_topics = NULL;
uint32 channel_users_modes_c = 0;
SilcChannelID **channel_ids = NULL;
&channels, &channel_users,
&channel_users_modes,
&channel_users_modes_c,
- &channel_ids);
+ &channel_topics,
+ &channel_ids, creation_time);
/* Get channels and channel users in global list */
- silc_server_announce_get_channels(server, server->global_list,
- &channels, &channel_users,
- &channel_users_modes,
- &channel_users_modes_c,
- &channel_ids);
+ if (server->server_type != SILC_SERVER)
+ silc_server_announce_get_channels(server, server->global_list,
+ &channels, &channel_users,
+ &channel_users_modes,
+ &channel_users_modes_c,
+ &channel_topics,
+ &channel_ids, creation_time);
if (channels) {
silc_buffer_push(channels, channels->data - channels->head);
SILC_LOG_HEXDUMP(("channels"), channels->data, channels->len);
/* Send the packet */
- silc_server_packet_send(server, server->router->connection,
+ silc_server_packet_send(server, remote,
SILC_PACKET_NEW_CHANNEL, SILC_PACKET_FLAG_LIST,
channels->data, channels->len,
FALSE);
channel_users->len);
/* Send the packet */
- silc_server_packet_send(server, server->router->connection,
+ silc_server_packet_send(server, remote,
SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
channel_users->data, channel_users->len,
FALSE);
channel_users_modes[i]->head);
SILC_LOG_HEXDUMP(("channel users modes"), channel_users_modes[i]->data,
channel_users_modes[i]->len);
- silc_server_packet_send_dest(server, server->router->connection,
+ silc_server_packet_send_dest(server, remote,
SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
channel_ids[i], SILC_ID_CHANNEL,
channel_users_modes[i]->data,
silc_buffer_free(channel_users_modes[i]);
}
silc_free(channel_users_modes);
- silc_free(channel_ids);
}
+
+ if (channel_topics) {
+ int i;
+
+ for (i = 0; i < channel_users_modes_c; i++) {
+ if (!channel_topics[i])
+ continue;
+
+ silc_buffer_push(channel_topics[i],
+ channel_topics[i]->data -
+ channel_topics[i]->head);
+ SILC_LOG_HEXDUMP(("channel topic"), channel_topics[i]->data,
+ channel_topics[i]->len);
+ silc_server_packet_send_dest(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel_ids[i], SILC_ID_CHANNEL,
+ channel_topics[i]->data,
+ channel_topics[i]->len,
+ FALSE);
+ silc_buffer_free(channel_topics[i]);
+ }
+ silc_free(channel_topics);
+ }
+
+ silc_free(channel_ids);
}
/* Failure timeout callback. If this is called then we will immediately
if (f->sock->protocol) {
f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
- silc_protocol_execute(f->sock->protocol, f->server->timeout_queue, 0, 0);
+ silc_protocol_execute(f->sock->protocol, f->server->schedule, 0, 0);
}
silc_free(f);
SilcBuffer client_id_list;
SilcBuffer client_mode_list;
SilcBuffer idp;
- uint32 list_count = 0;
+ uint32 list_count = 0, len = 0;
- /* XXX rewrite - this does not support IPv6 based Client ID's. */
+ silc_hash_table_list(channel->user_list, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chl))
+ len += (silc_id_get_len(chl->client->id, SILC_ID_CLIENT) + 4);
- client_id_list =
- silc_buffer_alloc((SILC_ID_CLIENT_LEN + 4) *
- silc_hash_table_count(channel->user_list));
+ client_id_list = silc_buffer_alloc(len);
client_mode_list =
silc_buffer_alloc(4 * silc_hash_table_count(channel->user_list));
silc_buffer_pull_tail(client_id_list, SILC_BUFFER_END(client_id_list));
silc_buffer_pull_tail(client_mode_list, SILC_BUFFER_END(client_mode_list));
+
silc_hash_table_list(channel->user_list, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
/* Client ID */
uint32 user_count)
{
int i;
+ uint16 idp_len;
+ uint32 mode;
+ SilcClientID *client_id;
+ SilcClientEntry client;
+ SilcIDCacheEntry cache;
+ bool global;
- /* Cache the received Client ID's and modes. This cache expires
- whenever server sends notify message to channel. It means two things;
- some user has joined or leaved the channel. XXX TODO! */
- for (i = 0; i < user_count; i++) {
- uint16 idp_len;
- uint32 mode;
- SilcClientID *client_id;
- SilcClientEntry client;
+ SILC_LOG_DEBUG(("Start"));
+ for (i = 0; i < user_count; i++) {
/* Client ID */
SILC_GET16_MSB(idp_len, user_list->data + 2);
idp_len += 4;
silc_free(client_id);
continue;
}
+
+ global = FALSE;
/* Check if we have this client cached already. */
client = silc_idlist_find_client_by_id(server->local_list, client_id,
- NULL);
- if (!client)
+ server->server_type, &cache);
+ if (!client) {
client = silc_idlist_find_client_by_id(server->global_list,
- client_id, NULL);
+ client_id, server->server_type,
+ &cache);
+ global = TRUE;
+ }
if (!client) {
/* If router did not find such Client ID in its lists then this must
be bogus client or some router in the net is buggy. */
global. */
client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
silc_id_dup(client_id, SILC_ID_CLIENT),
- sock->user_data, NULL);
+ sock->user_data, NULL, 0);
if (!client) {
+ SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
silc_free(client_id);
continue;
}
- client->data.registered = TRUE;
+ client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+ } else {
+ /* Found, if it is from global list we'll assure that we won't
+ expire it now that the entry is on channel. */
+ if (global)
+ cache->expire = 0;
}
silc_free(client_id);
/* If the destination belongs to our server we don't have to route
the packet anywhere but to send it to the local destination. */
- client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+ client = silc_idlist_find_client_by_id(server->local_list, id, TRUE, NULL);
if (client) {
silc_free(id);
- if (client && client->data.registered == FALSE)
- return NULL;
-
/* 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) {
- /* We are of course in this case the client's router thus the real
- "router" of the client is the server who owns the client. Thus
- we will send the packet to that server. */
+ /* We are of course in this case the client's router thus the route
+ to the client is the server who owns the client. So, we will send
+ the packet to that server. */
if (idata)
*idata = (SilcIDListData)client->router;
return client->router->connection;
/* 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) {
+ if (server->server_type != SILC_ROUTER && !server->standalone) {
silc_free(id);
if (idata)
*idata = (SilcIDListData)server->router;
and send the packet to fastest route. */
if (server->server_type == SILC_ROUTER && !server->standalone) {
/* Check first that the ID is valid */
- client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
+ client = silc_idlist_find_client_by_id(server->global_list, id,
+ TRUE, NULL);
if (client) {
SilcSocketConnection dst_sock;
while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
channel = chl->channel;
- if (channel->mode & SILC_CHANNEL_MODE_SECRET)
+ if (channel->mode & SILC_CHANNEL_MODE_SECRET ||
+ channel->mode & SILC_CHANNEL_MODE_PRIVATE)
continue;
cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
it using WHOIS command. */
SilcClientEntry silc_server_get_client_resolve(SilcServer server,
- SilcClientID *client_id)
+ SilcClientID *client_id,
+ bool *resolved)
{
SilcClientEntry client;
- client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
+ if (resolved)
+ *resolved = FALSE;
+
+ client = silc_idlist_find_client_by_id(server->local_list, client_id,
+ TRUE, NULL);
if (!client) {
client = silc_idlist_find_client_by_id(server->global_list,
- client_id, NULL);
+ client_id, TRUE, NULL);
if (!client && server->server_type == SILC_ROUTER)
return NULL;
}
if (!client || !client->nickname || !client->username) {
SilcBuffer buffer, idp;
+ client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
+ client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
+ client->resolve_cmd_ident = ++server->cmd_ident;
+
idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
- ++server->cmd_ident, 1,
+ server->cmd_ident, 1,
3, idp->data, idp->len);
silc_server_packet_send(server, client ? client->router->connection :
server->router->connection,
buffer->data, buffer->len, FALSE);
silc_buffer_free(idp);
silc_buffer_free(buffer);
+
+ if (resolved)
+ *resolved = TRUE;
+
+ return NULL;
}
return client;
sock->protocol = protocol;
/* Run the protocol */
- silc_protocol_execute(protocol, server->timeout_queue, 0, 0);
+ silc_protocol_execute(protocol, server->schedule, 0, 0);
/* Re-register re-key timeout */
- silc_task_register(server->timeout_queue, sock->sock,
- silc_server_rekey_callback,
- context, idata->rekey->timeout, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ silc_schedule_task_add(server->schedule, sock->sock,
+ silc_server_rekey_callback,
+ context, idata->rekey->timeout, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
}
/* The final callback for the REKEY protocol. This will actually take the
if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
- silc_protocol_cancel(protocol, server->timeout_queue);
+ SILC_LOG_ERROR(("Error occurred during rekey protocol"));
+ silc_protocol_cancel(protocol, server->schedule);
silc_protocol_free(protocol);
sock->protocol = NULL;
if (ctx->packet)
return;
}
+ /* Purge the outgoing data queue to assure that all rekey packets really
+ go to the network before we quit the protocol. */
+ silc_server_packet_queue_purge(server, sock);
+
/* Cleanup */
silc_protocol_free(protocol);
sock->protocol = NULL;