/* $Id: tracker.c 5078 2007-12-09 16:56:36Z morris $ */
/*
* Copyright (c) 2004-2007 Axel Andersson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include <stdarg.h>
#include <string.h>
#include <wired/wired.h>
#include "clients.h"
#include "commands.h"
#include "main.h"
#include "tracker.h"
#include "servers.h"
#include "settings.h"
static void wt_listen_thread(wi_runtime_instance_t *);
static void wt_receive_thread(wi_runtime_instance_t *);
static wi_array_t *wt_tcp_sockets;
static wi_array_t *wt_udp_sockets;
static wi_socket_context_t *wt_socket_context;
void wt_tracker_init(void) {
wi_enumerator_t *enumerator;
wi_array_t *array, *addresses;
wi_address_t *address;
wi_socket_t *tcp_socket, *udp_socket;
wi_string_t *ip, *string;
wi_address_family_t family;
wt_tcp_sockets = wi_array_init(wi_array_alloc());
wt_udp_sockets = wi_array_init(wi_array_alloc());
addresses = wi_array_init(wi_array_alloc());
if(wi_array_count(wt_settings.address) > 0) {
/* listen on configured addresses */
wi_array_rdlock(wt_settings.address);
enumerator = wi_array_data_enumerator(wt_settings.address);
while((string = wi_enumerator_next_data(enumerator))) {
array = wi_host_addresses(wi_host_with_string(string));
if(array)
wi_array_add_data_from_array(addresses, array);
else
wi_log_err(WI_STR("Could not resolve \"%@\": %m"), string);
}
wi_array_unlock(wt_settings.address);
} else {
/* add wildcard addresses */
wi_array_add_data(addresses, wi_address_wildcard_for_family(WI_ADDRESS_IPV4));
wi_array_add_data(addresses, wi_address_wildcard_for_family(WI_ADDRESS_IPV6));
}
enumerator = wi_array_data_enumerator(addresses);
while((address = wi_enumerator_next_data(enumerator))) {
ip = wi_address_string(address);
family = wi_address_family(address);
/* force address family? */
if(wt_address_family != WI_ADDRESS_NULL && family != wt_address_family)
continue;
/* create sockets */
wi_address_set_port(address, wt_settings.port);
tcp_socket = wi_autorelease(wi_socket_init_with_address(wi_socket_alloc(), address, WI_SOCKET_TCP));
udp_socket = wi_autorelease(wi_socket_init_with_address(wi_socket_alloc(), address, WI_SOCKET_UDP));
if(!tcp_socket || !udp_socket) {
wi_log_warn(WI_STR("Could not create socket for %@: %m"), ip);
continue;
}
wi_socket_set_interactive(tcp_socket, true);
/* listen on sockets */
if(!wi_socket_listen(tcp_socket, 5)) {
wi_log_warn(WI_STR("Could not listen on %@ port %u: %m"),
ip, wi_address_port(wi_socket_address(tcp_socket)));
continue;
}
if(!wi_socket_listen(udp_socket, 5)) {
wi_log_warn(WI_STR("Could not listen on %@ port %u: %m"),
ip, wi_address_port(wi_socket_address(udp_socket)));
continue;
}
/* add to list of sockets */
wi_array_add_data(wt_tcp_sockets, tcp_socket);
wi_array_add_data(wt_udp_sockets, udp_socket);
wi_log_info(WI_STR("Listening on %@ port %d"),
ip, wt_settings.port);
}
if(wi_array_count(wt_tcp_sockets) == 0 || wi_array_count(wt_udp_sockets) == 0)
wi_log_err(WI_STR("No addresses available for listening"));
wi_release(addresses);
}
void wt_tracker_create_threads(void) {
/* spawn the tracker threads */
if(!wi_thread_create_thread(wt_listen_thread, NULL) ||
!wi_thread_create_thread(wt_receive_thread, NULL))
wi_log_err(WI_STR("Could not create a thread: %m"));
}
void wt_tracker_apply_settings(void) {
/* set SSL cipher list */
if(wt_settings.cipher) {
if(!wi_socket_context_set_ssl_ciphers(wt_socket_context, wt_settings.cipher)) {
wi_log_err(WI_STR("Could not set SSL cipher list \"%@\""),
wt_settings.cipher);
}
}
/* load SSL certificate */
if(wt_settings.certificate) {
if(!wi_socket_context_set_ssl_certificate(wt_socket_context, wt_settings.certificate)) {
wi_log_err(WI_STR("Could not load certificate %@: %m"),
wt_settings.certificate);
}
if(!wi_socket_context_set_ssl_privkey(wt_socket_context, wt_settings.certificate)) {
wi_log_err(WI_STR("Could not load private key %@: %m"),
wt_settings.certificate);
}
}
}
#pragma mark -
void wt_ssl_init(void) {
unsigned char dh1024_p[] = {
0xBC,0xBB,0x2B,0x4F,0x58,0x58,0x9C,0x4D,0x46,0x0D,0xBB,0x9E,
0x4D,0x85,0x69,0x56,0x43,0x5E,0xFB,0xC8,0xF6,0xC0,0xAC,0x8E,
0xCB,0xF6,0x0B,0x38,0x8F,0x25,0xD6,0x7A,0xA1,0x26,0xC4,0x74,
0x74,0x98,0x96,0x3F,0x96,0x90,0x3B,0x00,0x6E,0xE3,0x0A,0x61,
0xA9,0xA2,0x62,0x49,0xDA,0x7D,0xE0,0x6B,0x8F,0xA7,0x89,0x7F,
0x41,0x09,0x09,0xA3,0xA2,0x5F,0x2C,0xD3,0x77,0x26,0x8D,0x81,
0x33,0x04,0xEF,0x40,0x75,0xB2,0xCF,0xBA,0xEF,0xD5,0x08,0xF4,
0x9E,0x30,0xD2,0x57,0x12,0xD6,0xEA,0x86,0xCA,0x10,0x7B,0x4B,
0x93,0x42,0x7E,0x79,0x42,0x36,0x5D,0x2B,0x23,0xDB,0x7E,0xAB,
0xDB,0xFD,0x1B,0xDA,0x86,0x49,0x15,0x92,0x41,0x56,0xDD,0x68,
0x2C,0x7F,0xAA,0x34,0x56,0x80,0xA5,0x8B };
unsigned char dh1024_g[] = { 0x02 };
wt_socket_context = wi_socket_context_init(wi_socket_context_alloc());
if(!wi_socket_context_set_ssl_type(wt_socket_context, WI_SOCKET_SSL_SERVER))
wi_log_err(WI_STR("Could not set SSL context: %m"));
if(!wi_socket_context_set_ssl_dh(wt_socket_context, dh1024_p, sizeof(dh1024_p), dh1024_g, sizeof(dh1024_g)))
wi_log_err(WI_STR("Could not set anonymous DH key: %m"));
}
#pragma mark -
static void wt_listen_thread(wi_runtime_instance_t *arg) {
wi_pool_t *pool;
wi_socket_t *socket;
wi_address_t *address;
wi_string_t *ip;
wt_client_t *client;
wi_uinteger_t i = 0;
pool = wi_pool_init(wi_pool_alloc());
while(wt_running) {
ip = NULL;
/* accept new client */
socket = wi_socket_accept_multiple(wt_tcp_sockets, wt_socket_context, 30.0, &address);
if(!address) {
wi_log_err(WI_STR("Could not accept a connection: %m"));
goto next;
}
ip = wi_address_string(address);
if(!socket) {
wi_log_err(WI_STR("Could not accept a connection for %@: %m"), ip);
goto next;
}
wi_socket_set_direction(socket, WI_SOCKET_READ);
wi_log_info(WI_STR("Connect from %@"), ip);
/* spawn a client thread */
client = wi_autorelease(wt_client_init_with_socket(wt_client_alloc(), socket));
if(!wi_thread_create_thread(wt_control_thread, client))
wi_log_err(WI_STR("Could not create a thread for %@: %m"), ip);
next:
if(++i % 100 == 0)
wi_pool_drain(pool);
}
wi_release(pool);
}
static void wt_receive_thread(wi_runtime_instance_t *arg) {
wi_pool_t *pool;
wi_array_t *arguments;
wi_address_t *address;
wi_string_t *ip, *command;
wi_time_interval_t interval;
wt_server_t *server;
char buffer[WI_SOCKET_BUFFER_SIZE];
wi_uinteger_t i = 0;
wi_integer_t bytes;
pool = wi_pool_init(wi_pool_alloc());
while(wt_running) {
command = NULL;
arguments = NULL;
ip = NULL;
/* read data */
bytes = wi_socket_recvfrom_multiple(wt_udp_sockets, wt_socket_context, buffer, sizeof(buffer), &address);
if(!address) {
wi_log_err(WI_STR("Could not receive data: %m"));
goto next;
}
ip = wi_address_string(address);
if(bytes < 0) {
wi_log_err(WI_STR("Could not receive data from %@: %m"), ip);
goto next;
}
/* parse command */
wi_parse_wired_command(wi_string_with_cstring(buffer), &command, &arguments);
if(wi_is_equal(command, WI_STR("UPDATE")) && wi_array_count(arguments) >= 6) {
server = wt_servers_server_with_key(WI_ARRAY(arguments, 0));
if(!server)
goto next;
if(!wi_is_equal(server->ip, ip))
goto next;
/* check update time */
interval = wi_time_interval();
if(server->update_time > 0.0 && wt_settings.maxupdatetime > 0.0) {
if(interval - server->update_time < wt_settings.maxupdatetime) {
wi_log_warn(WI_STR("Deleting \"%@\" with URL %@: Last update %.0f seconds ago considered too quick"),
server->name, server->url, interval - server->update_time);
wt_servers_remove_stats_for_server(server);
wt_servers_remove_server(server);
wt_servers_write_file();
goto next;
}
}
/* update server */
wt_servers_remove_stats_for_server(server);
server->users = wi_string_uint32(WI_ARRAY(arguments, 1));
server->guest = wi_string_bool(WI_ARRAY(arguments, 2));
server->download = wi_string_bool(WI_ARRAY(arguments, 3));
server->files = wi_string_uint32(WI_ARRAY(arguments, 4));
server->size = wi_string_uint64(WI_ARRAY(arguments, 5));
server->update_time = interval;
wt_servers_add_stats_for_server(server);
wi_lock_lock(wt_status_lock);
wt_write_status(false);
wi_lock_unlock(wt_status_lock);
}
next:
if(++i % 100 == 0)
wi_pool_drain(pool);
}
wi_release(pool);
}
#pragma mark -
void wt_reply(uint32_t n, wi_string_t *fmt, ...) {
wt_client_t *client = wt_client();
wi_string_t *string;
va_list ap;
va_start(ap, fmt);
string = wi_string_init_with_format_and_arguments(wi_string_alloc(), fmt, ap);
va_end(ap);
wi_socket_write(client->socket, 0.0, WI_STR("%u %@%c"), n, string, WT_MESSAGE_SEPARATOR);
wi_release(string);
}
syntax highlighted by Code2HTML, v. 0.9.1