+Mon Jul 9 18:28:34 EEST 2001 Pekka Riikonen <priikone@silcnet.org>
+
+ * The server now performs the incoming host IP/DNS lookup
+ using the silc_socket_host_lookup and thus does not block
+ the server anymore. Affected file silcd/server.c.
+
Mon Jul 9 13:40:03 EEST 2001 Pekka Riikonen <priikone@silcnet.org>
* Added new function silc_schedule_wakeup that is used in
o silcd/serverid.c and its routines supports only IPv4.
- o DNS/IP lookup blocks the server. This must be fixed. Check the
- resolver stuff (resolver(3), resolver(5)). Either we have to do the
- own resolver stuff (through scheduler, if possible without writing
- too much own stuff) or use threads.
-
o The backup router support described in the protocol specification
should be done at some point.
sock->protocol = NULL;
}
-/* Accepts new connections to the server. Accepting new connections are
- done in three parts to make it async. */
-
-SILC_TASK_CALLBACK(silc_server_accept_new_connection)
+/* 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. */
+
+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_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_task_register(server->timeout_queue, 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->timeout_queue);
+}
+
/* Second part of accepting new connection. Key exchange protocol has been
performed and now it is time to do little connection authentication
protocol to figure out whether this connection is client or server
*/
/* $Id$ */
-/* XXX on multi-threads the task queue locking is missing here. */
#include "silcincludes.h"
int max_fd;
void *wakeup;
SILC_MUTEX_DEFINE(lock);
+ bool is_locked;
};
/* Initializes the scheduler. Sets the non-timeout task queue hook and
void silc_schedule_stop(SilcSchedule schedule)
{
SILC_LOG_DEBUG(("Stopping scheduler"));
+ silc_mutex_lock(schedule->lock);
schedule->valid = FALSE;
+ silc_mutex_unlock(schedule->lock);
}
/* Sets a file descriptor to be listened by select() in scheduler. One can
here from the task queue. This macro is used by silc_schedule function.
We don't have to care about the tasks priority here because the tasks
are sorted in their priority order already at the registration phase. */
+/* This must be called holding the schedule->lock and the
+ schedule->fd_queue->lock. */
#define SILC_SCHEDULE_RUN_TASKS \
do { \
queue = schedule->fd_queue; \
if (queue && queue->valid == TRUE && queue->task) { \
task = queue->task; \
- \
- /* Walk thorugh all tasks in the particular task queue and \
- execute the callback functions of those tasks matching the \
- fd set by select(). */ \
- while(1) { \
- /* Validity of the task is checked always before and after \
- execution beacuse the task might have been unregistered \
+ \
+ /* Walk thorugh all tasks in the particular task queue and \
+ execute the callback functions of those tasks matching the \
+ fd set by select(). */ \
+ while(1) { \
+ /* Validity of the task is checked always before and after \
+ execution beacuse the task might have been unregistered \
in the callback function, ie. it is not valid anymore. */ \
- \
- if (task->valid) { \
- /* Task ready for reading */ \
- if ((FD_ISSET(task->fd, &schedule->in)) && \
- (task->iomask & (1L << SILC_TASK_READ))) { \
+ \
+ if (task->valid) { \
+ /* Task ready for reading */ \
+ if ((FD_ISSET(task->fd, &schedule->in)) && \
+ (task->iomask & (1L << SILC_TASK_READ))) { \
+ silc_mutex_unlock(queue->lock); \
+ silc_mutex_unlock(schedule->lock); \
task->callback(queue, SILC_TASK_READ, task->context, task->fd); \
- is_run = TRUE; \
- } \
- } \
+ silc_mutex_lock(schedule->lock); \
+ silc_mutex_lock(queue->lock); \
+ is_run = TRUE; \
+ } \
+ } \
\
if (task->valid) { \
/* Task ready for writing */ \
- if ((FD_ISSET(task->fd, &schedule->out)) && \
- (task->iomask & (1L << SILC_TASK_WRITE))) { \
+ if ((FD_ISSET(task->fd, &schedule->out)) && \
+ (task->iomask & (1L << SILC_TASK_WRITE))) { \
+ silc_mutex_unlock(queue->lock); \
+ silc_mutex_unlock(schedule->lock); \
task->callback(queue, SILC_TASK_WRITE, task->context, task->fd); \
- is_run = TRUE; \
- } \
+ silc_mutex_lock(schedule->lock); \
+ silc_mutex_lock(queue->lock); \
+ is_run = TRUE; \
+ } \
} \
\
- if (!task->valid) { \
- /* Invalid (unregistered) tasks are removed from the \
+ if (!task->valid) { \
+ /* Invalid (unregistered) tasks are removed from the \
task queue. */ \
if (queue->task == task->next) { \
silc_task_remove(queue, task); \
} \
\
task = task->next; \
- silc_task_remove(queue, task->prev); \
- continue; \
- } \
- \
- /* Break if there isn't more tasks in the queue */ \
- if (queue->task == task->next) \
+ silc_task_remove(queue, task->prev); \
+ continue; \
+ } \
+ \
+ /* Break if there isn't more tasks in the queue */ \
+ if (queue->task == task->next) \
break; \
- \
+ \
task = task->next; \
} \
} \
/* Selects tasks to be listened by select(). These are the non-timeout
tasks. This checks the scheduler's fd list. This macro is used by
silc_schedule function. */
+/* This must be called holding schedule->lock. */
#define SILC_SCHEDULE_SELECT_TASKS \
do { \
macro. This macro is used by silc_schedule function. We don't have to
care about priorities because tasks are already sorted in their priority
order at the registration phase. */
+/* This must be called with holding the schedule->lock and the
+ schedule->timeout_queue->lock */
#define SILC_SCHEDULE_RUN_TIMEOUT_TASKS \
do { \
\
/* Task ready for reading */ \
if (task->valid) { \
- if ((task->iomask & (1L << SILC_TASK_READ))) \
+ if ((task->iomask & (1L << SILC_TASK_READ))) { \
+ silc_mutex_unlock(queue->lock); \
+ silc_mutex_unlock(schedule->lock); \
task->callback(queue, SILC_TASK_READ, \
task->context, task->fd); \
+ silc_mutex_lock(schedule->lock); \
+ silc_mutex_lock(queue->lock); \
+ } \
} \
\
/* Task ready for writing */ \
if (task->valid) { \
- if ((task->iomask & (1L << SILC_TASK_WRITE))) \
- task->callback(queue, SILC_TASK_WRITE, \
- task->context, task->fd); \
+ if ((task->iomask & (1L << SILC_TASK_WRITE))) { \
+ silc_mutex_unlock(queue->lock); \
+ silc_mutex_unlock(schedule->lock); \
+ task->callback(queue, SILC_TASK_WRITE, \
+ task->context, task->fd); \
+ silc_mutex_lock(schedule->lock); \
+ silc_mutex_lock(queue->lock); \
+ } \
} \
\
/* Break if there isn't more tasks in the queue */ \
when at earliest some of the timeout tasks expire. If this is in the
past, they will be run now. This macro is used by the silc_schedule
function. */
+/* This must be called with holding the schedule->lock and the
+ schedule->timeout_queue->lock */
#define SILC_SCHEDULE_SELECT_TIMEOUT \
do { \
specific fd there wasn't other non-timeout tasks. This checks the earlier
set fd list, thus the generic tasks apply to all specified fd's. All the
generic tasks are executed at once. */
+/* This must be called holding the schedule->lock and the
+ schedule->generic_queue->lock. */
#define SILC_SCHEDULE_RUN_GENERIC_TASKS \
do { \
if (is_run == FALSE) { \
SILC_LOG_DEBUG(("Running generic tasks")); \
- silc_mutex_lock(schedule->lock); \
for (i = 0; i <= schedule->fd_list.last_fd; i++) \
if (schedule->fd_list.fd[i] != -1) { \
\
if (task->valid && schedule->fd_list.fd[i] != -1) { \
/* Task ready for reading */ \
if ((schedule->fd_list.fd[i] & (1L << SILC_TASK_READ))) { \
+ silc_mutex_unlock(queue->lock); \
silc_mutex_unlock(schedule->lock); \
task->callback(queue, SILC_TASK_READ, \
task->context, i); \
silc_mutex_lock(schedule->lock); \
+ silc_mutex_lock(queue->lock); \
} \
} \
\
if (task->valid && schedule->fd_list.fd[i] != -1) { \
/* Task ready for writing */ \
if ((schedule->fd_list.fd[i] & (1L << SILC_TASK_WRITE))) { \
+ silc_mutex_unlock(queue->lock); \
silc_mutex_unlock(schedule->lock); \
task->callback(queue, SILC_TASK_WRITE, \
task->context, i); \
silc_mutex_lock(schedule->lock); \
+ silc_mutex_lock(queue->lock); \
} \
} \
\
} \
} \
} \
- silc_mutex_unlock(schedule->lock); \
} \
} while(0)
SILC_LOG_DEBUG(("In scheduler loop"));
+ if (!schedule->is_locked)
+ silc_mutex_lock(schedule->lock);
+
/* If the task queues aren't initialized or we aren't valid anymore
we will return */
if ((!schedule->fd_queue && !schedule->timeout_queue
schedule->max_fd = -1;
is_run = FALSE;
- /* Calculate next timeout for select(). This is the timeout value
+ /* Calculate next timeout for silc_select(). This is the timeout value
when at earliest some of the timeout tasks expire. */
+ silc_mutex_lock(schedule->timeout_queue->lock);
SILC_SCHEDULE_SELECT_TIMEOUT;
-
- silc_mutex_lock(schedule->lock);
+ silc_mutex_unlock(schedule->timeout_queue->lock);
/* Add the file descriptors to the fd sets. These are the non-timeout
- tasks. The select() listens to these file descriptors. */
+ tasks. The silc_select() listens to these file descriptors. */
SILC_SCHEDULE_SELECT_TASKS;
if (schedule->max_fd == -1 && !schedule->timeout)
ret = silc_select(schedule->max_fd + 1, &schedule->in,
&schedule->out, 0, schedule->timeout);
+ silc_mutex_lock(schedule->lock);
+
switch (ret) {
case -1:
/* Error */
case 0:
/* Timeout */
SILC_LOG_DEBUG(("Running timeout tasks"));
+ silc_mutex_lock(schedule->timeout_queue->lock);
silc_gettimeofday(&curtime);
SILC_SCHEDULE_RUN_TIMEOUT_TASKS;
+ silc_mutex_unlock(schedule->timeout_queue->lock);
break;
default:
/* There is some data available now */
SILC_LOG_DEBUG(("Running non-timeout tasks"));
+ silc_mutex_lock(schedule->fd_queue->lock);
SILC_SCHEDULE_RUN_TASKS;
+ silc_mutex_unlock(schedule->fd_queue->lock);
+ silc_mutex_lock(schedule->generic_queue->lock);
SILC_SCHEDULE_RUN_GENERIC_TASKS;
+ silc_mutex_unlock(schedule->generic_queue->lock);
break;
}
+ if (!schedule->is_locked)
+ silc_mutex_unlock(schedule->lock);
+
return TRUE;
}
return;
}
+ silc_mutex_lock(schedule->lock);
+ schedule->is_locked = TRUE;
+
/* Start the scheduler loop */
while (silc_schedule_one(schedule, -1))
;
+
+ silc_mutex_unlock(schedule->lock);
}
/* Wakes up the scheduler. This is used only in multi-threaded
pthread_attr_destroy(&attr);
- SILC_LOG_DEBUG(("Created thread %p", (SilcThread)ret));
+ SILC_LOG_DEBUG(("Created thread %p", (SilcThread)thread));
- return (SilcThread)ret;
+ return (SilcThread)thread;
}
void silc_thread_exit(void *exit_value)