From 0a9eee0712fb83be3cd586c6b5b87821deaba702 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Mon, 9 Jul 2001 15:43:12 +0000 Subject: [PATCH] updates. --- CHANGES | 6 ++ TODO | 5 - apps/silcd/server.c | 145 ++++++++++++++++------------- lib/silcutil/silcschedule.c | 128 +++++++++++++++++-------- lib/silcutil/unix/silcunixthread.c | 4 +- 5 files changed, 177 insertions(+), 111 deletions(-) diff --git a/CHANGES b/CHANGES index a33e2368..278ea542 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,9 @@ +Mon Jul 9 18:28:34 EEST 2001 Pekka Riikonen + + * 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 * Added new function silc_schedule_wakeup that is used in diff --git a/TODO b/TODO index bcdeaa2e..745a026b 100644 --- a/TODO +++ b/TODO @@ -60,11 +60,6 @@ TODO/bugs In SILC Server 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. diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 1160f10b..707e1d2d 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -931,87 +931,58 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) 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"); @@ -1023,23 +994,23 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection) 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++; @@ -1052,7 +1023,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection) 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; @@ -1065,7 +1036,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection) 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 @@ -1073,13 +1044,57 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection) 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 diff --git a/lib/silcutil/silcschedule.c b/lib/silcutil/silcschedule.c index 260ed50d..ee1063d4 100644 --- a/lib/silcutil/silcschedule.c +++ b/lib/silcutil/silcschedule.c @@ -18,7 +18,6 @@ */ /* $Id$ */ -/* XXX on multi-threads the task queue locking is missing here. */ #include "silcincludes.h" @@ -136,6 +135,7 @@ struct SilcScheduleStruct { int max_fd; void *wakeup; SILC_MUTEX_DEFINE(lock); + bool is_locked; }; /* Initializes the scheduler. Sets the non-timeout task queue hook and @@ -241,7 +241,9 @@ bool silc_schedule_uninit(SilcSchedule schedule) 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 @@ -286,41 +288,51 @@ void silc_schedule_unset_listen_fd(SilcSchedule schedule, int fd) 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); \ @@ -328,14 +340,14 @@ do { \ } \ \ 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; \ } \ } \ @@ -344,6 +356,7 @@ do { \ /* 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 { \ @@ -371,6 +384,8 @@ 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 { \ @@ -386,16 +401,26 @@ 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 */ \ @@ -426,6 +451,8 @@ do { \ 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 { \ @@ -490,12 +517,13 @@ 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) { \ \ @@ -520,20 +548,24 @@ do { \ 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); \ } \ } \ \ @@ -559,7 +591,6 @@ do { \ } \ } \ } \ - silc_mutex_unlock(schedule->lock); \ } \ } while(0) @@ -574,6 +605,9 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs) 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 @@ -588,14 +622,14 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs) 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) @@ -621,6 +655,8 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs) ret = silc_select(schedule->max_fd + 1, &schedule->in, &schedule->out, 0, schedule->timeout); + silc_mutex_lock(schedule->lock); + switch (ret) { case -1: /* Error */ @@ -631,18 +667,27 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs) 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; } @@ -659,9 +704,14 @@ void silc_schedule(SilcSchedule schedule) 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 diff --git a/lib/silcutil/unix/silcunixthread.c b/lib/silcutil/unix/silcunixthread.c index 9cd68c3b..7751603f 100644 --- a/lib/silcutil/unix/silcunixthread.c +++ b/lib/silcutil/unix/silcunixthread.c @@ -58,9 +58,9 @@ SilcThread silc_thread_create(SilcThreadStart start_func, void *context, 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) -- 2.24.0