From 2910c571b0ed787f8ff1cfd38ffe86c4a6033eec Mon Sep 17 00:00:00 2001 From: Jan Vidar Krey Date: Wed, 7 Oct 2009 17:37:31 +0200 Subject: [PATCH] Fix protocol probe. Will detect ADC and TLS handshake - any other request will simply cause the hub to close the connection. Fix problems with write events not being processed due to a read event taking presendence. Fix bug #86: Windows does not have "getrlimit()". --- GNUmakefile | 1 + src/core/auth.c | 7 ++- src/core/config.c | 2 +- src/core/hub.c | 12 ++-- src/core/hubio.c | 53 +++--------------- src/core/hubio.h | 5 +- src/core/netevent.c | 65 +++++++++------------- src/core/probe.c | 115 +++++++++++++++++++++++++++++++++++++++ src/core/probe.h | 35 ++++++++++++ src/core/route.c | 3 + src/core/user.c | 11 ++-- src/core/user.h | 3 +- src/network/connection.c | 43 ++++++++++++++- src/network/connection.h | 8 +++ src/network/network.c | 9 ++- src/tools/adcrush.c | 1 + 16 files changed, 267 insertions(+), 106 deletions(-) create mode 100644 src/core/probe.c create mode 100644 src/core/probe.h diff --git a/GNUmakefile b/GNUmakefile index 63248bd..d9cefa5 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -135,6 +135,7 @@ libuhub_SOURCES := \ src/core/hubevent.c \ src/core/hubio.c \ src/core/inf.c \ + src/core/probe.c \ src/util/ipcalc.c \ src/util/list.c \ src/util/log.c \ diff --git a/src/core/auth.c b/src/core/auth.c index 9cb7660..ca5c950 100644 --- a/src/core/auth.c +++ b/src/core/auth.c @@ -434,9 +434,10 @@ const char* acl_password_generate_challenge(struct acl_handle* acl, struct hub_u char buf[32]; uint64_t tiger_res[3]; static char tiger_buf[MAX_CID_LEN+1]; - - snprintf(buf, 32, "%d%d%d", (int) user->tm_connected, (int) user->id.sid, (int) user->connection->sd); - + + // FIXME: Generate a better nonce scheme. + snprintf(buf, 32, "%p%d%d", user, (int) user->id.sid, (int) user->connection->sd); + tiger((uint64_t*) buf, strlen(buf), (uint64_t*) tiger_res); base32_encode((unsigned char*) tiger_res, TIGERSIZE, tiger_buf); tiger_buf[MAX_CID_LEN] = 0; diff --git a/src/core/config.c b/src/core/config.c index ee345b7..7015aa5 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -417,7 +417,7 @@ void dump_config(struct hub_config* config, int ignore_defaults) DUMP_BOOL(registered_users_only, DEF_REGISTERED_USERS_ONLY); #ifdef SSL_SUPPORT - DUMP_BOOL(tls_enable, DEF_TLS_ENABLE); + DUMP_BOOL(tls_enable, DEF_TLS_ENABLE); DUMP_BOOL(tls_require, DEF_TLS_REQUIRE); DUMP_STR (tls_certificate, DEF_TLS_CERTIFICATE); DUMP_STR (tls_private_key, DEF_TLS_PRIVATE_KEY); diff --git a/src/core/hub.c b/src/core/hub.c index 0bb528e..313a196 100644 --- a/src/core/hub.c +++ b/src/core/hub.c @@ -555,7 +555,7 @@ struct hub_info* hub_start_service(struct hub_config* config) net_close(server_tcp); return 0; } - + event_set(&hub->ev_accept, hub->fd_tcp, EV_READ | EV_PERSIST, net_on_accept, hub); if (event_add(&hub->ev_accept, NULL) == -1) { @@ -572,7 +572,7 @@ struct hub_info* hub_start_service(struct hub_config* config) net_close(server_tcp); return 0; } - + hub->recvbuf = hub_malloc(MAX_RECV_BUF); hub->sendbuf = hub_malloc(MAX_SEND_BUF); if (!hub->recvbuf || !hub->sendbuf) @@ -650,7 +650,7 @@ void hub_set_variables(struct hub_info* hub, struct acl_handle* acl) adc_msg_add_named_argument(hub->command_info, ADC_INF_FLAG_DESCRIPTION, tmp); hub_free(tmp); } - + /* (Re-)read the message of the day */ hub->command_motd = 0; fd = open(hub->config->file_motd, 0); @@ -1033,7 +1033,11 @@ void hub_disconnect_user(struct hub_info* hub, struct hub_user* user, int reason /* stop reading from user */ net_shutdown_r(user->connection->sd); - net_con_close(user->connection); + if (net_con_close(user->connection)) + { + hub_free(user->connection); + } + user->connection = 0; LOG_TRACE("hub_disconnect_user(), user=%p, reason=%d, state=%d", user, reason, user->state); diff --git a/src/core/hubio.c b/src/core/hubio.c index 14ea99d..1481451 100644 --- a/src/core/hubio.c +++ b/src/core/hubio.c @@ -146,58 +146,21 @@ void hub_sendq_remove(struct hub_sendq* q, struct adc_message* msg) q->offset = 0; } -int hub_sendq_send(struct hub_sendq* q, hub_recvq_write w, void* data) +int hub_sendq_send(struct hub_sendq* q, struct hub_user* user) { - int ret = 0; - size_t bytes = 0; - size_t offset = q->offset; // offset into first message. - size_t remain = 0; - size_t length = 0; - char* sbuf = g_hub->sendbuf; - size_t max_send_buf = 4096; - - /* Copy as many messages possible into global send queue */ struct adc_message* msg = list_get_first(q->queue); - while (msg) - { - length = MIN(msg->length - offset, (max_send_buf-1) - bytes); - memcpy(sbuf + bytes, msg->cache + offset, length); - bytes += length; - - if (length < (msg->length - offset)) - break; - offset = 0; - msg = list_get_next(q->queue); - } + if (!msg) return 0; - msg = list_get_first(q->queue); - - /* Send as much as possible */ - ret = w(data, sbuf, bytes); + int ret = net_con_send(user->connection, msg->cache + q->offset, msg->length - q->offset); if (ret > 0) { -#ifdef SSL_SUPPORT - q->last_send = ret; -#endif + q->offset += ret; + if (msg->length - q->offset > 0) + return 0; - /* Remove messages sent */ - offset = q->offset; - remain = ret; - - while (msg) - { - length = msg->length - offset; - if (length >= remain) - { - q->offset += remain; - break; - } - remain -= length; - hub_sendq_remove(q, msg); - msg = list_get_next(q->queue); - offset = 0; - } + hub_sendq_remove(q, msg); + return 1; } return ret; } diff --git a/src/core/hubio.h b/src/core/hubio.h index eae3fe6..677a604 100644 --- a/src/core/hubio.h +++ b/src/core/hubio.h @@ -58,10 +58,9 @@ extern void hub_sendq_add(struct hub_sendq*, struct adc_message* msg); /** * Process the send queue, and send as many messages as possible. - * @returns the number of bytes sent. - * FIXME: send error not handled here! + * @returns -1 on error, 0 if unable to send more, 1 if more can be sent. */ -extern int hub_sendq_send(struct hub_sendq*, hub_recvq_write, void* data); +extern int hub_sendq_send(struct hub_sendq*, struct hub_user*); /** * @returns 1 if send queue is empty, 0 otherwise. diff --git a/src/core/netevent.c b/src/core/netevent.c index 24eba8d..fab9229 100644 --- a/src/core/netevent.c +++ b/src/core/netevent.c @@ -17,8 +17,9 @@ * */ -#include "uhub.h" +#include #include "hubio.h" +#include "probe.h" /* FIXME: This should not be needed! */ extern struct hub_info* g_hub; @@ -52,21 +53,6 @@ void debug_sendq_recv(struct hub_user* user, int received, int max, const char* } #endif -int net_user_send(void* ptr, const void* buf, size_t len) -{ - struct hub_user* user = (struct hub_user*) ptr; - int ret = net_con_send(user->connection, buf, len); -#ifdef DEBUG_SENDQ - debug_sendq_send(user, ret, len); -#endif - if (ret > 0) - return ret; - if (ret == 0) - return -2; - else - return -1; -} - int net_user_recv(void* ptr, void* buf, size_t len) { struct hub_user* user = (struct hub_user*) ptr; @@ -161,13 +147,11 @@ int handle_net_write(struct hub_user* user) int ret = 0; while (hub_sendq_get_bytes(user->send_queue)) { - ret = hub_sendq_send(user->send_queue, net_user_send, user); + ret = hub_sendq_send(user->send_queue, user); if (ret <= 0) break; } - if (ret == -1) - return quit_disconnected; if (ret < 0) return quit_socket_error; @@ -189,9 +173,12 @@ void net_event(struct net_connection* con, int event, void *arg) if (event == NET_EVENT_DESTROYED) { - printf("NET_EVENT_DESTROYED\n"); - hub_free(user->connection); - user->connection = 0; + printf("NET_EVENT_DESTROYED: con=%p, user=%p\n", con, user); + if (user) + { + user->connection = 0; + } + hub_free(con); return; } @@ -217,26 +204,32 @@ void net_event(struct net_connection* con, int event, void *arg) return; } } - else if (event & NET_EVENT_READ) + + if (event & NET_EVENT_READ) { flag_close = handle_net_read(user); - } - else if (event & NET_EVENT_WRITE) - { - flag_close = handle_net_write(user); + if (flag_close) + { + hub_disconnect_user(g_hub, user, flag_close); + return; + } } - if (flag_close) + if (event & NET_EVENT_WRITE) { - hub_disconnect_user(g_hub, user, flag_close); - return; + flag_close = handle_net_write(user); + if (flag_close) + { + hub_disconnect_user(g_hub, user, flag_close); + return; + } } } void net_on_accept(int server_fd, short event, void *arg) { struct hub_info* hub = (struct hub_info*) arg; - struct hub_user* user = 0; + struct hub_probe* probe = 0; struct ip_addr_encap ipaddr; const char* addr; @@ -269,17 +262,13 @@ void net_on_accept(int server_fd, short event, void *arg) continue; } - user = user_create(hub, fd, &ipaddr); - if (!user) + probe = probe_create(hub, fd, &ipaddr); + if (!probe) { - LOG_ERROR("Unable to create user after socket accepted. Out of memory?"); + LOG_ERROR("Unable to create probe after socket accepted. Out of memory?"); net_close(fd); break; } - -#ifdef SSL_SUPPORT - net_con_ssl_handshake(&user->net.connection, NET_CON_SSL_MODE_SERVER); -#endif } } diff --git a/src/core/probe.c b/src/core/probe.c new file mode 100644 index 0000000..a3f2c69 --- /dev/null +++ b/src/core/probe.c @@ -0,0 +1,115 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "uhub.h" +#include "probe.h" + +#define PROBE_RECV_SIZE 12 +static char probe_recvbuf[PROBE_RECV_SIZE]; + +static void probe_net_event(struct net_connection* con, int events, void *arg) +{ + struct hub_probe* probe = (struct hub_probe*) con->ptr; + + if (events == NET_EVENT_DESTROYED) + { + if (probe) + { + probe->connection = 0; + } + hub_free(con); + return; + } + + if (events == NET_EVENT_SOCKERROR || events == NET_EVENT_CLOSED || events == NET_EVENT_TIMEOUT) + { + probe_destroy(probe); + return; + } + + if (events & NET_EVENT_READ) + { + int bytes = net_con_peek(con, probe_recvbuf, PROBE_RECV_SIZE); + if (bytes < 0) + { + probe_destroy(probe); + return; + } + + if (bytes >= 4) + { + if (memcmp(probe_recvbuf, "HSUP", 4) == 0) + { + LOG_TRACE("Probed ADC"); + if (user_create(probe->hub, probe->connection, &probe->addr)) + { + probe->connection = 0; + } + probe_destroy(probe); + return; + } + +#ifdef SSL_SUPPORT + if (bytes >= 11 + probe_recvbuf[0] == 22 && + probe_recvbuf[1] == 3 && /* protocol major version */ + probe_recvbuf[5] == 1 && /* message type */ + probe_recvbuf[9] == probe_recvbuf[1] && + probe_recvbuf[10] == probe_recvbuf[2]) + { + LOG_TRACE("Probed TLS %d.%d connection", (int) probe_recvbuf[1], (int) probe_recvbuf[2]); + + net_con_ssl_handshake(con, NET_CON_SSL_MODE_SERVER); + return; + } +#endif + probe_destroy(probe); + return; + } + } +} + +struct hub_probe* probe_create(struct hub_info* hub, int sd, struct ip_addr_encap* addr) +{ + struct hub_probe* probe = (struct hub_probe*) hub_malloc_zero(sizeof(struct hub_probe)); + + if (probe == NULL) + return NULL; /* OOM */ + + probe->hub = hub; + probe->connection = (struct net_connection*) hub_malloc(sizeof(struct net_connection)); + net_con_initialize(probe->connection, sd, probe_net_event, probe, NET_EVENT_READ); + net_con_set_timeout(probe->connection, TIMEOUT_CONNECTED); + + memcpy(&probe->addr, addr, sizeof(struct ip_addr_encap)); + return probe; +} + +void probe_destroy(struct hub_probe* probe) +{ + if (probe->connection) + { + if (net_con_close(probe->connection)) + { + hub_free(probe->connection); + } + probe->connection = 0; + } + hub_free(probe); +} diff --git a/src/core/probe.h b/src/core/probe.h new file mode 100644 index 0000000..f4db19f --- /dev/null +++ b/src/core/probe.h @@ -0,0 +1,35 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef HAVE_UHUB_PROBE_H +#define HAVE_UHUB_PROBE_H + +#include "uhub.h" + +struct hub_probe +{ + struct hub_info* hub; /** The hub instance this probe belong to */ + struct net_connection* connection; /** Connection data */ + struct ip_addr_encap addr; /** IP address */ +}; + +extern struct hub_probe* probe_create(struct hub_info* hub, int sd, struct ip_addr_encap* addr); +extern void probe_destroy(struct hub_probe* probe); + +#endif /* HAVE_UHUB_PROBE_H */ diff --git a/src/core/route.c b/src/core/route.c index c9c366b..bbaa1c7 100644 --- a/src/core/route.c +++ b/src/core/route.c @@ -97,6 +97,9 @@ int route_to_user(struct hub_info* hub, struct hub_user* user, struct adc_messag free(data); #endif + if (!user->connection) + return 0; + if (hub_sendq_is_empty(user->send_queue) && !user_flag_get(user, flag_pipeline)) { /* Perform oportunistic write */ diff --git a/src/core/user.c b/src/core/user.c index 8e92185..0213ed6 100644 --- a/src/core/user.c +++ b/src/core/user.c @@ -35,27 +35,24 @@ static const char* user_log_str(struct hub_user* user) } #endif -struct hub_user* user_create(struct hub_info* hub, int sd, struct ip_addr_encap* addr) +struct hub_user* user_create(struct hub_info* hub, struct net_connection* con, struct ip_addr_encap* addr) { struct hub_user* user = NULL; - LOG_TRACE("user_create(), hub=%p, sd=%d", hub, sd); + LOG_TRACE("user_create(), hub=%p, con[sd=%d]", hub, con->sd); user = (struct hub_user*) hub_malloc_zero(sizeof(struct hub_user)); if (user == NULL) return NULL; /* OOM */ - user->tm_connected = time(NULL); user->send_queue = hub_sendq_create(); user->recv_queue = hub_recvq_create(); - user->connection = (struct net_connection*) hub_malloc(sizeof(struct net_connection)); - net_con_initialize(user->connection, sd, net_event, user, NET_EVENT_READ); - net_con_set_timeout(user->connection, TIMEOUT_CONNECTED); + user->connection = con; + net_con_reinitialize(user->connection, net_event, user, NET_EVENT_READ); memcpy(&user->id.addr, addr, sizeof(struct ip_addr_encap)); - user_set_state(user, state_protocol); return user; } diff --git a/src/core/user.h b/src/core/user.h index 7a8b44d..5510d81 100644 --- a/src/core/user.h +++ b/src/core/user.h @@ -111,7 +111,6 @@ struct hub_user struct hub_recvq* recv_queue; struct hub_sendq* send_queue; struct net_connection* connection; /** Connection data */ - time_t tm_connected; /** time when user connected */ struct hub_user_limits limits; /** Data used for limitation */ enum user_quit_reason quit_reason; /** Quit reason (see user_quit_reason) */ @@ -128,7 +127,7 @@ struct hub_user * @param sd socket descriptor associated with the user * @return User object or NULL if not enough memory is available. */ -extern struct hub_user* user_create(struct hub_info* hub, int sd, struct ip_addr_encap* addr); +extern struct hub_user* user_create(struct hub_info* hub, struct net_connection* con, struct ip_addr_encap* addr); /** * Delete a user. diff --git a/src/network/connection.c b/src/network/connection.c index bebab0f..e8a0358 100644 --- a/src/network/connection.c +++ b/src/network/connection.c @@ -180,7 +180,13 @@ void net_con_initialize(struct net_connection* con, int sd, net_connection_cb ca #endif } - +void net_con_reinitialize(struct net_connection* con, net_connection_cb callback, const void* ptr, int events) +{ + uhub_assert(con); + con->callback = callback; + con->ptr = (void*) ptr; + net_con_update(con, events); +} void net_con_update(struct net_connection* con, int ev) { @@ -393,6 +399,41 @@ ssize_t net_con_recv(struct net_connection* con, void* buf, size_t len) #endif } +ssize_t net_con_peek(struct net_connection* con, void* buf, size_t len) +{ + uhub_assert(con); + +#ifdef SSL_SUPPORT + if (!con->ssl) + { +#endif + int ret = net_recv(con->sd, buf, len, MSG_PEEK); +#ifdef NETWORK_DUMP_DEBUG + LOG_PROTO("net_recv: ret=%d (MSG_PEEK)", ret); +#endif + if (ret > 0) + { + con->last_recv = time(0); + } + else if (ret == -1 && (net_error() == EWOULDBLOCK || net_error() == EINTR)) + { + return 0; + } + else + { + return -1; + } + return ret; +#ifdef SSL_SUPPORT + } + else + { + // FIXME: Not able to do this! + return 0; + } +#endif +} + void net_con_set_timeout(struct net_connection* con, int seconds) { uhub_assert(con); diff --git a/src/network/connection.h b/src/network/connection.h index 96cf919..402c6a2 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -64,6 +64,7 @@ struct net_connection }; extern void net_con_initialize(struct net_connection* con, int sd, net_connection_cb callback, const void* ptr, int events); +extern void net_con_reinitialize(struct net_connection* con, net_connection_cb callback, const void* ptr, int events); extern void net_con_update(struct net_connection* con, int events); /** @@ -92,6 +93,13 @@ extern ssize_t net_con_send(struct net_connection* con, const void* buf, size_t */ extern ssize_t net_con_recv(struct net_connection* con, void* buf, size_t len); +/** + * Receive data without removing them from the recv() buffer. + * NOTE: This does not currently work for SSL connections after the SSL handshake has been + * performed. + */ +extern ssize_t net_con_peek(struct net_connection* con, void* buf, size_t len); + /** * Set timeout for connetion. * diff --git a/src/network/network.c b/src/network/network.c index 312fb3b..6a90541 100644 --- a/src/network/network.c +++ b/src/network/network.c @@ -80,9 +80,14 @@ size_t net_get_max_sockets() } LOG_ERROR("getrlimit() failed"); #else - LOG_ERROR("System does not have getrlimit(): constrained to 1024 sockets"); -#endif /* HAVE_GETRLIMIT */ +#ifdef WIN32 + LOG_WARN("Windows system, limited to 4096 connections."); + return 4096; +#else + LOG_WARN("System does not have getrlimit(): constrained to 1024 sockets"); return 1024; +#endif +#endif /* HAVE_GETRLIMIT */ } diff --git a/src/tools/adcrush.c b/src/tools/adcrush.c index 92d3f9f..eb72450 100644 --- a/src/tools/adcrush.c +++ b/src/tools/adcrush.c @@ -337,6 +337,7 @@ void runloop(size_t clients) { ADC_client_destroy(client[n]); free(client[n]); + client[n] = 0; } }