X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=apps%2Fsilcd%2Fserver.c;h=f0ed13f8e6b1950302bfdb8045d0f1e150ffcdb1;hb=d47a87b03b846e2333ef57b2c0d81f1644992964;hp=4543cc28cabe994c8d984903fed396cbfa01e4fa;hpb=4e64dd02ccecc9c497b8db8ecb09d017b9cef9a4;p=silc.git diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 4543cc28..f0ed13f8 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -111,25 +111,24 @@ void silc_server_free(SilcServer server) 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); assert(server->config); /* Set public and private keys */ - if (!server->config->server_keys || - !server->config->server_keys->public_key || - !server->config->server_keys->private_key) { + if (!server->config->server_info || + !server->config->server_info->public_key || + !server->config->server_info->private_key) { SILC_LOG_ERROR(("Server public key and/or private key does not exist")); return FALSE; } - server->public_key = server->config->server_keys->public_key; - server->private_key = server->config->server_keys->private_key; + server->public_key = server->config->server_info->public_key; + server->private_key = server->config->server_info->private_key; /* XXX After server is made as Silc Server Library this can be given as argument, for now this is hard coded */ @@ -141,18 +140,14 @@ int silc_server_init(SilcServer server) server->params->protocol_timeout = 60; server->params->require_reverse_mapping = FALSE; - /* 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)) + if (!silc_server_config_register_ciphers(server)) silc_cipher_register_default(); - if (!silc_server_config_register_pkcs(server->config)) + if (!silc_server_config_register_pkcs(server)) silc_pkcs_register_default(); - if (!silc_server_config_register_hashfuncs(server->config)) + if (!silc_server_config_register_hashfuncs(server)) silc_hash_register_default(); - if (!silc_server_config_register_hmacs(server->config)) + if (!silc_server_config_register_hmacs(server)) silc_hmac_register_default(); /* Initialize random number generator for the server. */ @@ -164,9 +159,6 @@ int silc_server_init(SilcServer server) silc_hash_alloc("md5", &server->md5hash); silc_hash_alloc("sha1", &server->sha1hash); - /* Initialize none cipher */ - silc_cipher_alloc("none", &server->none_cipher); - /* 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); @@ -175,23 +167,23 @@ int silc_server_init(SilcServer server) /* Create a listening server. Note that our server can listen on multiple ports. All listeners are created here and now. */ sock_count = 0; - listen = server->config->listen_port; - while(listen) { + while (1) { int tmp; - tmp = silc_net_create_server(server->config->listen_port->port, - server->config->listen_port->listener_ip); + tmp = silc_net_create_server(server->config->server_info->port, + server->config->server_info->server_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)); + SILC_LOG_ERROR(("Could not create server listener: %s on %hd", + server->config->server_info->server_ip, + server->config->server_info->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; sock_count++; - listen = listen->next; + break; } /* Initialize ID caches */ @@ -222,11 +214,32 @@ int silc_server_init(SilcServer server) 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); @@ -248,33 +261,10 @@ int silc_server_init(SilcServer server) } 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; } @@ -285,7 +275,7 @@ int silc_server_init(SilcServer server) server->schedule = silc_schedule_init(SILC_SERVER_MAX_CONNECTIONS); if (!server->schedule) goto err0; - + /* 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 @@ -306,10 +296,13 @@ int silc_server_init(SilcServer server) 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) { - SilcServerConfigSectionServerConnection *ptr = server->config->servers; + SilcServerConfigSectionServer *ptr = server->config->servers; server->server_type = SILC_ROUTER; while (ptr) { @@ -330,18 +323,20 @@ int silc_server_init(SilcServer server) purge = silc_calloc(1, sizeof(*purge)); purge->cache = server->local_list->clients; purge->schedule = server->schedule; + purge->timeout = 600; silc_schedule_task_add(purge->schedule, 0, silc_idlist_purge, - (void *)purge, 600, 0, + (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->schedule = server->schedule; + purge->timeout = 300; silc_schedule_task_add(purge->schedule, 0, silc_idlist_purge, - (void *)purge, 300, 0, + (void *)purge, purge->timeout, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); SILC_LOG_DEBUG(("Server initialized")); @@ -356,27 +351,26 @@ int silc_server_init(SilcServer server) return FALSE; } -/* Fork server to background and set gid+uid to non-root */ +/* Fork server to background */ void silc_server_daemonise(SilcServer server) { int i; - i = fork (); + SILC_LOG_DEBUG(("Forking SILC server to background")); - if (i) { - if (i > 0) { - if (geteuid()) - SILC_LOG_DEBUG(("Server started as user")); - else - SILC_LOG_DEBUG(("Server started as root. Dropping privileges.")); + i = fork(); - SILC_LOG_DEBUG(("Forking SILC server to background")); - exit(0); - } else { - SILC_LOG_DEBUG(("fork() failed, cannot proceed")); - exit(1); - } + 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(); } @@ -387,13 +381,11 @@ void silc_server_drop(SilcServer server) { /* Are we executing silcd as root or a regular user? */ if (!geteuid()) { - struct passwd *pw; struct group *gr; char *user, *group; - if (!server->config->identity || !server->config->identity->user || - !server->config->identity->group) { + if (!server->config->server_info->user || !server->config->server_info->group) { fprintf(stderr, "Error:" "\tSILC server must not be run as root. For the security of your\n" "\tsystem it is strongly suggested that you run SILC under dedicated\n" @@ -403,8 +395,8 @@ void silc_server_drop(SilcServer server) } /* Get the values given for user and group in configuration file */ - user=server->config->identity->user; - group=server->config->identity->group; + user=server->config->server_info->user; + group=server->config->server_info->group; /* Check whether the user/group information is text */ if (atoi(user)!=0 || atoi(group)!=0) { @@ -500,8 +492,11 @@ void silc_server_stop(SilcServer server) { SILC_LOG_DEBUG(("Stopping server")); - silc_schedule_stop(server->schedule); - silc_schedule_uninit(server->schedule); + if (server->schedule) { + silc_schedule_stop(server->schedule); + silc_schedule_uninit(server->schedule); + server->schedule = NULL; + } silc_server_protocols_unregister(); @@ -521,6 +516,10 @@ void silc_server_start_key_exchange(SilcServer server, SilcServerKEInternalContext *proto_ctx; void *context; + /* Cancel any possible retry timeouts */ + silc_schedule_task_del_by_callback(server->schedule, + silc_server_connect_router); + /* Set socket options */ silc_net_set_socket_nonblock(sock); silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); @@ -603,6 +602,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry) 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; } @@ -628,15 +629,17 @@ SILC_TASK_CALLBACK(silc_server_connect_router) server->router_connect = time(0); /* Connect to remote host */ - sock = silc_net_create_connection(server->config->listen_port->local_ip, + sock = silc_net_create_connection(server->config->server_info->server_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); + SILC_LOG_ERROR(("Could not connect to router %s:%d", + sconn->remote_host, sconn->remote_port)); + if (!sconn->no_reconnect) + silc_schedule_task_add(server->schedule, fd, + silc_server_connect_to_router_retry, + context, 0, 1, SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); return; } @@ -653,7 +656,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router) { SilcServer server = (SilcServer)context; SilcServerConnection sconn; - SilcServerConfigSectionServerConnection *ptr; + SilcServerConfigSectionRouter *ptr; SILC_LOG_DEBUG(("Connecting to router(s)")); @@ -686,6 +689,9 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router) sconn->backup_replace_port = ptr->backup_replace_port; } + if (!server->router_conn && !sconn->backup) + server->router_conn = sconn; + silc_schedule_task_add(server->schedule, fd, silc_server_connect_router, (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, @@ -717,7 +723,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second) SilcServerConnection sconn = (SilcServerConnection)ctx->context; SilcSocketConnection sock = ctx->sock; SilcServerConnAuthInternalContext *proto_ctx; - SilcServerConfigSectionServerConnection *conn = NULL; + SilcServerConfigSectionRouter *conn = NULL; SILC_LOG_DEBUG(("Start")); @@ -937,7 +943,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) timeout!! */ hb_context = silc_calloc(1, sizeof(*hb_context)); hb_context->server = server; - silc_socket_set_heartbeat(sock, 600, hb_context, + silc_socket_set_heartbeat(sock, 300, hb_context, silc_server_perform_heartbeat, server->schedule); @@ -985,6 +991,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) 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) @@ -994,6 +1002,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) silc_packet_context_free(ctx->packet); if (ctx->ske) silc_ske_free(ctx->ske); + silc_free(ctx->auth_data); silc_free(ctx); } @@ -1009,7 +1018,7 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock, SilcServer server = (SilcServer)context; SilcServerKEInternalContext *proto_ctx; void *cconfig, *sconfig, *rconfig; - SilcServerConfigSectionDenyConnection *deny; + SilcServerConfigSectionDeny *deny; int port; SILC_LOG_DEBUG(("Start")); @@ -1040,16 +1049,16 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock, 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, sock->ip, port); + deny = silc_server_config_find_denied(server->config, sock->ip, port); if (!deny) - deny = silc_server_config_denied_conn(server->config, sock->hostname, + deny = silc_server_config_find_denied(server->config, sock->hostname, port); if (deny) { /* The connection is denied */ SILC_LOG_INFO(("Connection %s (%s) is denied", sock->hostname, sock->ip)); - silc_server_disconnect_remote(server, sock, deny->comment ? - deny->comment : + silc_server_disconnect_remote(server, sock, deny->reason ? + deny->reason : "Server closed connection: " "Connection refused"); server->stat.conn_failures++; @@ -1059,9 +1068,9 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock, /* Check whether we have configred this sort of connection at all. We have to check all configurations since we don't know what type of connection this is. */ - if (!(cconfig = silc_server_config_find_client_conn(server->config, + if (!(cconfig = silc_server_config_find_client(server->config, sock->ip, port))) - cconfig = silc_server_config_find_client_conn(server->config, + cconfig = silc_server_config_find_client(server->config, sock->hostname, port); if (!(sconfig = silc_server_config_find_server_conn(server->config, @@ -1076,6 +1085,8 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock, sock->hostname, port); if (!cconfig && !sconfig && !rconfig) { + SILC_LOG_INFO(("Connection %s (%s) is not allowed", + sock->hostname, sock->ip)); silc_server_disconnect_remote(server, sock, "Server closed connection: " "Connection refused"); @@ -1279,7 +1290,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) SilcSocketConnection sock = ctx->sock; SilcServerHBContext hb_context; SilcUnknownEntry entry = (SilcUnknownEntry)sock->user_data; - void *id_entry = NULL; + void *id_entry; SILC_LOG_DEBUG(("Start")); @@ -1317,7 +1328,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) 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); @@ -1341,7 +1352,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) case SILC_SOCKET_TYPE_ROUTER: { SilcServerEntry new_server; - SilcServerConfigSectionServerConnection *conn = + /* XXX FIXME: Now server and router has different table, so this is probably broken. */ + SilcServerConfigSectionRouter *conn = ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? ctx->sconfig : ctx->rconfig; @@ -1421,15 +1433,15 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) break; } default: + goto out; break; } sock->type = ctx->conn_type; /* Add the common data structure to the ID entry. */ - if (id_entry) - silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data); - + silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data); + /* Add to sockets internal pointer for fast referencing */ silc_free(sock->user_data); sock->user_data = id_entry; @@ -1442,7 +1454,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) timeout!! */ hb_context = silc_calloc(1, sizeof(*hb_context)); hb_context->server = server; - silc_socket_set_heartbeat(sock, 600, hb_context, + silc_socket_set_heartbeat(sock, 400, hb_context, silc_server_perform_heartbeat, server->schedule); @@ -1542,7 +1554,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process) 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; } @@ -1550,8 +1562,20 @@ SILC_TASK_CALLBACK(silc_server_packet_process) SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock)); SILC_SET_DISCONNECTING(sock); - if (sock->user_data) - silc_server_free_sock_user_data(server, sock); + 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); + silc_server_close_connection(server, sock); return; } @@ -1575,9 +1599,23 @@ SILC_TASK_CALLBACK(silc_server_packet_process) /* Process the packet. This will call the parser that will then decrypt and parse the packet. */ - silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ? - TRUE : FALSE, cipher, hmac, sequence, - silc_server_packet_parse, server); + 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); + + if (sock->user_data) + silc_server_free_sock_user_data(server, sock, NULL); + silc_server_close_connection(server, sock); + } } /* Parses whole packet, received earlier. */ @@ -1629,7 +1667,7 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real) 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, @@ -2169,7 +2207,7 @@ void silc_server_packet_parse_type(SilcServer server, /* Creates connection to a remote router. */ void silc_server_create_connection(SilcServer server, - char *remote_host, uint32 port) + const char *remote_host, uint32 port) { SilcServerConnection sconn; @@ -2178,6 +2216,7 @@ void silc_server_create_connection(SilcServer server, sconn->server = server; sconn->remote_host = strdup(remote_host); sconn->remote_port = port; + sconn->no_reconnect = TRUE; silc_schedule_task_add(server->schedule, 0, silc_server_connect_router, @@ -2286,7 +2325,7 @@ void silc_server_free_client_data(SilcServer server, SilcSocketConnection sock, SilcClientEntry client, int notify, - char *signoff) + const char *signoff) { FreeClientInternal i = silc_calloc(1, sizeof(*i)); @@ -2306,11 +2345,19 @@ void silc_server_free_client_data(SilcServer server, /* 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); + /* Update statistics */ + server->stat.my_clients--; + server->stat.clients--; + if (server->server_type == SILC_ROUTER) + server->stat.cell_clients--; + SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR); + SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR); + /* We will not delete the client entry right away. We will take it into history (for WHOWAS command) for 5 minutes */ i->server = server; @@ -2322,12 +2369,7 @@ void silc_server_free_client_data(SilcServer server, 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--; - server->stat.clients--; - if (server->server_type == SILC_ROUTER) - server->stat.cell_clients--; + client->mode = 0; } /* Frees user_data pointer from socket connection object. This also sends @@ -2335,7 +2377,8 @@ void silc_server_free_client_data(SilcServer server, entities. */ void silc_server_free_sock_user_data(SilcServer server, - SilcSocketConnection sock) + SilcSocketConnection sock, + const char *signoff_message) { SILC_LOG_DEBUG(("Start")); @@ -2343,7 +2386,8 @@ void silc_server_free_sock_user_data(SilcServer server, 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: @@ -2361,7 +2405,7 @@ void silc_server_free_sock_user_data(SilcServer server, /* 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, + silc_server_connect_to_router, server, 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); @@ -2555,6 +2599,7 @@ void silc_server_remove_from_channels(SilcServer server, silc_hash_table_del(channel->user_list, chl2->client); silc_free(chl2); } + silc_hash_table_list_reset(&htl2); continue; } @@ -2579,7 +2624,7 @@ void silc_server_remove_from_channels(SilcServer server, if (keygen && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) { /* Re-generate channel key */ if (!silc_server_create_channel_key(server, channel, 0)) - return; + goto out; /* Send the channel key to the channel. The key of course is not sent to the client who was removed from the channel. */ @@ -2589,6 +2634,8 @@ void silc_server_remove_from_channels(SilcServer server, } } + out: + silc_hash_table_list_reset(&htl); silc_buffer_free(clidp); } @@ -2675,6 +2722,7 @@ int silc_server_remove_from_one_channel(SilcServer server, silc_hash_table_del(channel->user_list, chl2->client); silc_free(chl2); } + silc_hash_table_list_reset(&htl2); return FALSE; } @@ -2713,13 +2761,15 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote) /* 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->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"); @@ -2761,7 +2811,7 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server, channel_name = strdup(channel_name); - /* Create the channel */ + /* Create the channel ID */ if (!silc_id_create_channel_id(server, router_id, server->rng, &channel_id)) { silc_free(channel_name); @@ -2769,13 +2819,16 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server, silc_hmac_free(newhmac); return NULL; } + + /* 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); silc_cipher_free(key); silc_hmac_free(newhmac); + silc_free(channel_id); return NULL; } @@ -2785,11 +2838,7 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server, /* Now create the actual key material */ 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); + silc_idlist_del_channel(server->local_list, entry); return NULL; } @@ -2842,8 +2891,10 @@ silc_server_create_new_channel_with_id(SilcServer server, /* 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_cipher_free(key); + silc_hmac_free(newhmac); silc_free(channel_name); return NULL; } @@ -2851,7 +2902,7 @@ silc_server_create_new_channel_with_id(SilcServer server, /* Now create the actual key material */ if (!silc_server_create_channel_key(server, entry, silc_cipher_get_key_len(key) / 8)) { - silc_free(channel_name); + silc_idlist_del_channel(server->local_list, entry); return NULL; } @@ -2978,10 +3029,10 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server, SILC_LOG_DEBUG(("Start")); /* Decode channel key payload */ - payload = silc_channel_key_payload_parse(key_payload->data, + 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; } @@ -3001,7 +3052,8 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server, if (!channel) { channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL); if (!channel) { - SILC_LOG_ERROR(("Received key for non-existent channel")); + SILC_LOG_ERROR(("Received key for non-existent channel %s", + silc_id_render(id, SILC_ID_CHANNEL))); goto out; } } @@ -3086,8 +3138,7 @@ void silc_server_perform_heartbeat(SilcSocketConnection sock, { SilcServerHBContext hb = (SilcServerHBContext)hb_context; - SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname, - sock->ip)); + SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname, sock->ip)); /* Send the heartbeat */ silc_server_send_heartbeat(hb->server, sock); @@ -3143,6 +3194,19 @@ static void silc_server_announce_get_servers(SilcServer server, } } +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. If `creation_time' is non-zero then only the servers that has been created after the `creation_time' @@ -3186,12 +3250,15 @@ void silc_server_announce_servers(SilcServer server, bool global, static void silc_server_announce_get_clients(SilcServer server, SilcIDList id_list, 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)) { @@ -3214,6 +3281,20 @@ static void silc_server_announce_get_clients(SilcServer server, 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)) @@ -3235,17 +3316,18 @@ void silc_server_announce_clients(SilcServer server, 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, creation_time); + &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, creation_time); + &clients, &umodes, creation_time); if (clients) { silc_buffer_push(clients, clients->data - clients->head); @@ -3258,19 +3340,36 @@ void silc_server_announce_clients(SilcServer server, 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 p; + SilcBuffer chidp; - va_start(ap, argc); - p = silc_notify_payload_encode(notify, argc, ap); - va_end(ap); - - return p; + 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'. */ @@ -3333,6 +3432,7 @@ void silc_server_announce_get_channel_users(SilcServer server, silc_buffer_free(clidp); } + silc_hash_table_list_reset(&htl); silc_buffer_free(chidp); } @@ -3346,6 +3446,7 @@ void silc_server_announce_get_channels(SilcServer server, SilcBuffer *channel_users, SilcBuffer **channel_users_modes, uint32 *channel_users_modes_c, + SilcBuffer **channel_topics, SilcChannelID ***channel_ids, unsigned long creation_time) { @@ -3395,6 +3496,7 @@ void silc_server_announce_get_channels(SilcServer server, silc_buffer_pull(*channels, len); } + /* Channel user modes */ *channel_users_modes = silc_realloc(*channel_users_modes, sizeof(**channel_users_modes) * (i + 1)); @@ -3406,6 +3508,13 @@ void silc_server_announce_get_channels(SilcServer server, channel_users, &(*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)) @@ -3432,6 +3541,7 @@ void silc_server_announce_channels(SilcServer server, { 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; @@ -3442,6 +3552,7 @@ void silc_server_announce_channels(SilcServer server, &channels, &channel_users, &channel_users_modes, &channel_users_modes_c, + &channel_topics, &channel_ids, creation_time); /* Get channels and channel users in global list */ @@ -3450,6 +3561,7 @@ void silc_server_announce_channels(SilcServer server, &channels, &channel_users, &channel_users_modes, &channel_users_modes_c, + &channel_topics, &channel_ids, creation_time); if (channels) { @@ -3483,6 +3595,8 @@ void silc_server_announce_channels(SilcServer server, int i; for (i = 0; i < channel_users_modes_c; i++) { + if (!channel_users_modes[i]) + continue; silc_buffer_push(channel_users_modes[i], channel_users_modes[i]->data - channel_users_modes[i]->head); @@ -3497,8 +3611,32 @@ void silc_server_announce_channels(SilcServer server, 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 @@ -3538,6 +3676,7 @@ void silc_server_get_users_on_channel(SilcServer server, 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); + silc_hash_table_list_reset(&htl); client_id_list = silc_buffer_alloc(len); client_mode_list = @@ -3559,6 +3698,7 @@ void silc_server_get_users_on_channel(SilcServer server, list_count++; } + silc_hash_table_list_reset(&htl); silc_buffer_push(client_id_list, client_id_list->data - client_id_list->head); silc_buffer_push(client_mode_list, @@ -3580,13 +3720,16 @@ void silc_server_save_users_on_channel(SilcServer server, uint32 user_count) { int i; + uint16 idp_len; + uint32 mode; + SilcClientID *client_id; + SilcClientEntry client; + SilcIDCacheEntry cache; + bool global; - 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; @@ -3603,14 +3746,18 @@ void silc_server_save_users_on_channel(SilcServer server, 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, - server->server_type, NULL); - if (!client) + server->server_type, &cache); + if (!client) { client = silc_idlist_find_client_by_id(server->global_list, client_id, server->server_type, - NULL); + &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. */ @@ -3624,7 +3771,7 @@ void silc_server_save_users_on_channel(SilcServer server, 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); @@ -3632,6 +3779,11 @@ void silc_server_save_users_on_channel(SilcServer server, } 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); @@ -3771,6 +3923,7 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server, silc_buffer_pull(buffer, len); silc_free(cid); } + silc_hash_table_list_reset(&htl); if (buffer) silc_buffer_push(buffer, buffer->data - buffer->head); @@ -3782,10 +3935,14 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server, it using WHOIS command. */ SilcClientEntry silc_server_get_client_resolve(SilcServer server, - SilcClientID *client_id) + SilcClientID *client_id, + bool *resolved) { SilcClientEntry client; + if (resolved) + *resolved = FALSE; + client = silc_idlist_find_client_by_id(server->local_list, client_id, TRUE, NULL); if (!client) { @@ -3815,6 +3972,10 @@ SilcClientEntry silc_server_get_client_resolve(SilcServer server, buffer->data, buffer->len, FALSE); silc_buffer_free(idp); silc_buffer_free(buffer); + + if (resolved) + *resolved = TRUE; + return NULL; }