2010-01-21 00:12:57 +00:00
|
|
|
/*
|
|
|
|
* uhub - A tiny ADC p2p connection hub
|
2014-05-14 09:38:08 +00:00
|
|
|
* Copyright (C) 2007-2014, Jan Vidar Krey
|
2010-01-21 00:12:57 +00:00
|
|
|
*
|
|
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "uhub.h"
|
|
|
|
|
|
|
|
#include "network/connection.h"
|
|
|
|
|
2010-02-10 20:57:27 +00:00
|
|
|
struct net_backend;
|
|
|
|
struct net_connection;
|
|
|
|
|
2010-01-21 00:12:57 +00:00
|
|
|
struct net_cleanup_handler
|
|
|
|
{
|
|
|
|
size_t num;
|
|
|
|
size_t max;
|
|
|
|
struct net_connection** queue;
|
|
|
|
};
|
|
|
|
|
2010-02-10 20:57:27 +00:00
|
|
|
struct net_backend
|
|
|
|
{
|
|
|
|
struct net_backend_common common;
|
|
|
|
time_t now; /* the time now (used for timeout handling) */
|
|
|
|
struct timeout_queue timeout_queue; /* used for timeout handling */
|
|
|
|
struct net_cleanup_handler* cleaner; /* handler to cleanup connections at a safe point */
|
|
|
|
struct net_backend_handler handler; /* backend event handler */
|
|
|
|
struct net_backend* data; /* backend specific data */
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct net_backend* g_backend;
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef USE_EPOLL
|
|
|
|
extern struct net_backend* net_backend_init_epoll(struct net_backend_handler*, struct net_backend_common*);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef USE_KQUEUE
|
|
|
|
extern struct net_backend* net_backend_init_kqueue(struct net_backend_handler*, struct net_backend_common*);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef USE_SELECT
|
|
|
|
extern struct net_backend* net_backend_init_select(struct net_backend_handler*, struct net_backend_common*);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static net_backend_init_t net_backend_init_funcs[] = {
|
|
|
|
#ifdef USE_EPOLL
|
|
|
|
net_backend_init_epoll,
|
|
|
|
#endif
|
|
|
|
#ifdef USE_KQUEUE
|
|
|
|
net_backend_init_kqueue,
|
|
|
|
#endif
|
|
|
|
#ifdef USE_SELECT
|
|
|
|
net_backend_init_select,
|
|
|
|
#endif
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
int net_backend_init()
|
|
|
|
{
|
|
|
|
size_t n;
|
2011-09-05 21:16:15 +00:00
|
|
|
g_backend = (struct net_backend*) hub_malloc_zero(sizeof(struct net_backend));
|
2010-02-10 20:57:27 +00:00
|
|
|
g_backend->common.num = 0;
|
|
|
|
g_backend->common.max = net_get_max_sockets();
|
|
|
|
g_backend->now = time(0);
|
2010-03-12 23:16:15 +00:00
|
|
|
timeout_queue_initialize(&g_backend->timeout_queue, g_backend->now, 120); /* FIXME: max 120 secs! */
|
2010-02-10 20:57:27 +00:00
|
|
|
g_backend->cleaner = net_cleanup_initialize(g_backend->common.max);
|
|
|
|
|
2010-05-25 20:47:30 +00:00
|
|
|
for (n = 0; net_backend_init_funcs[n]; n++)
|
2010-02-10 20:57:27 +00:00
|
|
|
{
|
|
|
|
g_backend->data = net_backend_init_funcs[n](&g_backend->handler, &g_backend->common);
|
|
|
|
if (g_backend->data)
|
|
|
|
{
|
|
|
|
LOG_DEBUG("Initialized %s network backend.", g_backend->handler.backend_name());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LOG_FATAL("Unable to find a suitable network backend");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_backend_shutdown()
|
|
|
|
{
|
|
|
|
g_backend->handler.backend_shutdown(g_backend->data);
|
|
|
|
timeout_queue_shutdown(&g_backend->timeout_queue);
|
|
|
|
net_cleanup_shutdown(g_backend->cleaner);
|
|
|
|
hub_free(g_backend);
|
|
|
|
g_backend = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Fix: Rework SSL poll event handling to avoid infinite loops
The downstream connection callback must only be invoked when the event
that SSL requests for the connection to make progress has actually
occured. Otherwise, the downstream callback might do nothing but
re-queue an unrelated event (e.g. in user_net_io_want_write), and the
event loop comes around instantly while making no progress. Track the
SSL-requested events separately and deliver the required downstream
event when they fire.
Sample strace:
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: User writes data, OpenSSL tries to write data
write(96, <snip>..., 170) = -1 EAGAIN (Resource temporarily unavailable)
: handle_openssl_error requests NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLOUT, {u32=96, u64=96}}) = 0
: User callback then requests NET_EVENT_READ|NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLIN|EPOLLOUT, {u32=96, u64=96}}) =
: Data available for *reading*
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: again...
2014-04-03 20:24:41 +00:00
|
|
|
void net_backend_update(struct net_connection* con, int events)
|
2010-02-10 20:57:27 +00:00
|
|
|
{
|
|
|
|
g_backend->handler.con_mod(g_backend->data, con, events);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct net_connection* net_con_create()
|
|
|
|
{
|
|
|
|
return g_backend->handler.con_create(g_backend->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct timeout_queue* net_backend_get_timeout_queue()
|
|
|
|
{
|
|
|
|
if (!g_backend)
|
|
|
|
return 0;
|
|
|
|
return &g_backend->timeout_queue;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Process the network backend.
|
|
|
|
*/
|
|
|
|
int net_backend_process()
|
|
|
|
{
|
2013-02-05 21:38:13 +00:00
|
|
|
int res = 0;
|
2010-02-10 20:57:27 +00:00
|
|
|
size_t secs = timeout_queue_get_next_timeout(&g_backend->timeout_queue, g_backend->now);
|
|
|
|
|
2013-02-05 21:38:13 +00:00
|
|
|
if (g_backend->common.num)
|
|
|
|
res = g_backend->handler.backend_poll(g_backend->data, secs * 1000);
|
2010-02-10 20:57:27 +00:00
|
|
|
|
|
|
|
g_backend->now = time(0);
|
|
|
|
timeout_queue_process(&g_backend->timeout_queue, g_backend->now);
|
|
|
|
|
|
|
|
if (res == -1)
|
|
|
|
{
|
|
|
|
LOG_WARN("backend error.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-24 21:19:14 +00:00
|
|
|
// Process pending DNS results
|
2014-08-05 14:21:56 +00:00
|
|
|
// net_dns_process();
|
2012-10-24 21:19:14 +00:00
|
|
|
|
2010-02-10 20:57:27 +00:00
|
|
|
g_backend->handler.backend_process(g_backend->data, res);
|
|
|
|
|
|
|
|
net_cleanup_process(g_backend->cleaner);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-04-05 13:51:18 +00:00
|
|
|
time_t net_get_time()
|
|
|
|
{
|
|
|
|
return g_backend->now;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-10 20:57:27 +00:00
|
|
|
void net_con_initialize(struct net_connection* con, int sd, net_connection_cb callback, const void* ptr, int events)
|
|
|
|
{
|
|
|
|
g_backend->handler.con_init(g_backend->data, con, sd, callback, ptr);
|
|
|
|
|
|
|
|
net_set_nonblocking(con->sd, 1);
|
|
|
|
net_set_nosigpipe(con->sd, 1);
|
|
|
|
|
|
|
|
g_backend->handler.con_add(g_backend->data, con, events);
|
|
|
|
g_backend->common.num++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_con_close(struct net_connection* con)
|
|
|
|
{
|
|
|
|
if (con->flags & NET_CLEANUP)
|
|
|
|
return;
|
|
|
|
|
|
|
|
g_backend->common.num--;
|
|
|
|
net_con_clear_timeout(con);
|
|
|
|
|
|
|
|
g_backend->handler.con_del(g_backend->data, con);
|
|
|
|
|
2010-08-24 20:58:10 +00:00
|
|
|
#ifdef SSL_SUPPORT
|
2010-08-24 21:18:42 +00:00
|
|
|
if (con->ssl)
|
2012-10-12 12:24:03 +00:00
|
|
|
net_ssl_shutdown(con);
|
2012-10-02 21:59:11 +00:00
|
|
|
#endif /* SSL_SUPPORT */
|
2010-08-24 20:58:10 +00:00
|
|
|
|
2010-02-10 20:57:27 +00:00
|
|
|
net_close(con->sd);
|
|
|
|
con->sd = -1;
|
|
|
|
|
|
|
|
net_cleanup_delayed_free(g_backend->cleaner, con);
|
|
|
|
}
|
|
|
|
|
2010-01-21 00:12:57 +00:00
|
|
|
struct net_cleanup_handler* net_cleanup_initialize(size_t max)
|
|
|
|
{
|
|
|
|
struct net_cleanup_handler* handler = (struct net_cleanup_handler*) hub_malloc(sizeof(struct net_cleanup_handler));
|
|
|
|
handler->num = 0;
|
|
|
|
handler->max = max;
|
|
|
|
handler->queue = hub_malloc_zero(sizeof(struct net_connection*) * max);
|
|
|
|
return handler;
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_cleanup_shutdown(struct net_cleanup_handler* handler)
|
|
|
|
{
|
2010-08-24 20:58:10 +00:00
|
|
|
net_cleanup_process(handler);
|
2010-01-21 00:12:57 +00:00
|
|
|
hub_free(handler->queue);
|
|
|
|
hub_free(handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_cleanup_delayed_free(struct net_cleanup_handler* handler, struct net_connection* con)
|
|
|
|
{
|
|
|
|
handler->queue[handler->num++] = con;
|
|
|
|
con->flags |= NET_CLEANUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_cleanup_process(struct net_cleanup_handler* handler)
|
|
|
|
{
|
|
|
|
size_t n;
|
|
|
|
for (n = 0; n < handler->num; n++)
|
|
|
|
{
|
|
|
|
struct net_connection* con = handler->queue[n];
|
|
|
|
LOG_TRACE("net_cleanup_process: free: %p", con);
|
2011-12-02 12:03:17 +00:00
|
|
|
net_con_destroy(con);
|
2010-01-21 00:12:57 +00:00
|
|
|
}
|
|
|
|
handler->num = 0;
|
|
|
|
}
|
|
|
|
|