2009-08-02 19:37:55 +00:00
|
|
|
/*
|
|
|
|
* uhub - A tiny ADC p2p connection hub
|
2014-05-14 09:38:08 +00:00
|
|
|
* Copyright (C) 2007-2014, Jan Vidar Krey
|
2009-08-02 19:37:55 +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"
|
2010-01-19 13:53:47 +00:00
|
|
|
#include "network/common.h"
|
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
|
|
|
#include "network/backend.h"
|
2010-01-19 13:53:47 +00:00
|
|
|
|
2012-10-16 18:15:38 +00:00
|
|
|
static int is_blocked_or_interrupted()
|
|
|
|
{
|
|
|
|
int err = net_error();
|
|
|
|
return
|
|
|
|
#ifdef WINSOCK
|
|
|
|
err == WSAEWOULDBLOCK
|
|
|
|
#else
|
|
|
|
err == EWOULDBLOCK
|
|
|
|
#endif
|
|
|
|
|| err == EINTR;
|
|
|
|
}
|
|
|
|
|
2010-01-19 13:53:47 +00:00
|
|
|
ssize_t net_con_send(struct net_connection* con, const void* buf, size_t len)
|
|
|
|
{
|
2010-01-22 22:40:41 +00:00
|
|
|
int ret;
|
|
|
|
#ifdef SSL_SUPPORT
|
|
|
|
if (!con->ssl)
|
2010-01-19 13:53:47 +00:00
|
|
|
{
|
2010-01-22 22:40:41 +00:00
|
|
|
#endif
|
|
|
|
ret = net_send(con->sd, buf, len, UHUB_SEND_SIGNAL);
|
|
|
|
if (ret == -1)
|
|
|
|
{
|
2012-10-16 18:15:38 +00:00
|
|
|
if (is_blocked_or_interrupted())
|
2010-01-22 22:40:41 +00:00
|
|
|
return 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef SSL_SUPPORT
|
2010-01-19 13:53:47 +00:00
|
|
|
}
|
2010-01-22 22:40:41 +00:00
|
|
|
else
|
|
|
|
{
|
2012-10-12 12:24:03 +00:00
|
|
|
ret = net_ssl_send(con, buf, len);
|
2010-01-22 22:40:41 +00:00
|
|
|
}
|
2012-10-02 21:59:11 +00:00
|
|
|
#endif /* SSL_SUPPORT */
|
2010-01-19 13:53:47 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t net_con_recv(struct net_connection* con, void* buf, size_t len)
|
|
|
|
{
|
2010-01-22 22:40:41 +00:00
|
|
|
int ret;
|
2010-01-19 13:53:47 +00:00
|
|
|
#ifdef SSL_SUPPORT
|
2012-10-12 12:24:03 +00:00
|
|
|
if (!con->ssl)
|
2010-01-19 13:53:47 +00:00
|
|
|
{
|
|
|
|
#endif
|
2010-01-22 22:40:41 +00:00
|
|
|
ret = net_recv(con->sd, buf, len, 0);
|
2010-01-19 13:53:47 +00:00
|
|
|
if (ret == -1)
|
|
|
|
{
|
2012-10-16 18:15:38 +00:00
|
|
|
if (is_blocked_or_interrupted())
|
2010-01-19 13:53:47 +00:00
|
|
|
return 0;
|
|
|
|
return -net_error();
|
|
|
|
}
|
|
|
|
else if (ret == 0)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef SSL_SUPPORT
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-10-12 12:24:03 +00:00
|
|
|
ret = net_ssl_recv(con, buf, len);
|
2010-01-19 13:53:47 +00:00
|
|
|
}
|
2012-10-02 21:59:11 +00:00
|
|
|
#endif /* SSL_SUPPORT */
|
2010-01-22 22:40:41 +00:00
|
|
|
return ret;
|
2010-01-19 13:53:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t net_con_peek(struct net_connection* con, void* buf, size_t len)
|
|
|
|
{
|
2010-01-20 16:26:23 +00:00
|
|
|
int ret = net_recv(con->sd, buf, len, MSG_PEEK);
|
2010-01-19 13:53:47 +00:00
|
|
|
if (ret == -1)
|
|
|
|
{
|
2012-10-16 18:15:38 +00:00
|
|
|
if (is_blocked_or_interrupted())
|
2010-01-19 13:53:47 +00:00
|
|
|
return 0;
|
|
|
|
return -net_error();
|
|
|
|
}
|
|
|
|
else if (ret == 0)
|
|
|
|
return -1;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-01-20 16:26:23 +00:00
|
|
|
#ifdef SSL_SUPPORT
|
2012-10-02 21:59:11 +00:00
|
|
|
|
2010-01-20 16:26:23 +00:00
|
|
|
int net_con_is_ssl(struct net_connection* con)
|
|
|
|
{
|
2012-10-12 12:24:03 +00:00
|
|
|
return !!con->ssl;
|
2010-01-20 16:26:23 +00:00
|
|
|
}
|
|
|
|
#endif /* SSL_SUPPORT */
|
2009-10-09 18:32:23 +00:00
|
|
|
|
2010-01-20 16:26:23 +00:00
|
|
|
int net_con_get_sd(struct net_connection* con)
|
|
|
|
{
|
|
|
|
return con->sd;
|
|
|
|
}
|
|
|
|
|
|
|
|
void* net_con_get_ptr(struct net_connection* con)
|
|
|
|
{
|
|
|
|
return con->ptr;
|
|
|
|
}
|
|
|
|
|
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_con_update(struct net_connection* con, int events)
|
|
|
|
{
|
|
|
|
#ifdef SSL_SUPPORT
|
|
|
|
if (con->ssl)
|
|
|
|
net_ssl_update(con, events);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
net_backend_update(con, events);
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_con_reinitialize(struct net_connection* con, net_connection_cb callback, const void* ptr, int events)
|
|
|
|
{
|
|
|
|
con->callback = callback;
|
|
|
|
con->ptr = (void*) ptr;
|
|
|
|
net_con_update(con, events);
|
|
|
|
}
|
|
|
|
|
2010-02-10 20:57:27 +00:00
|
|
|
void net_con_destroy(struct net_connection* con)
|
|
|
|
{
|
2010-08-23 19:40:07 +00:00
|
|
|
#ifdef SSL_SUPPORT
|
2014-07-29 15:35:14 +00:00
|
|
|
if (con && con->ssl)
|
2012-10-12 12:24:03 +00:00
|
|
|
net_ssl_destroy(con);
|
2010-08-23 19:40:07 +00:00
|
|
|
#endif
|
2010-02-10 20:57:27 +00:00
|
|
|
hub_free(con);
|
|
|
|
}
|
|
|
|
|
2010-01-20 16:26:23 +00:00
|
|
|
void net_con_callback(struct net_connection* con, int events)
|
|
|
|
{
|
2010-01-22 22:40:41 +00:00
|
|
|
if (con->flags & NET_CLEANUP)
|
2010-01-22 17:52:38 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (events == NET_EVENT_TIMEOUT)
|
2010-01-20 16:26:23 +00:00
|
|
|
{
|
2012-09-27 08:45:39 +00:00
|
|
|
LOG_TRACE("net_con_callback(%p, TIMEOUT)", con);
|
2010-01-20 16:26:23 +00:00
|
|
|
con->callback(con, events, con->ptr);
|
2010-01-22 17:52:38 +00:00
|
|
|
return;
|
2010-01-20 16:26:23 +00:00
|
|
|
}
|
2010-01-22 17:52:38 +00:00
|
|
|
|
|
|
|
#ifdef SSL_SUPPORT
|
2012-10-16 18:15:38 +00:00
|
|
|
if (con->ssl)
|
2012-10-12 12:24:03 +00:00
|
|
|
net_ssl_callback(con, events);
|
2012-10-16 18:15:38 +00:00
|
|
|
else
|
2010-01-22 17:52:38 +00:00
|
|
|
#endif
|
2012-10-16 18:15:38 +00:00
|
|
|
con->callback(con, events, con->ptr);
|
2010-01-20 16:26:23 +00:00
|
|
|
}
|
2009-08-02 19:37:55 +00:00
|
|
|
|
2013-02-05 21:40:20 +00:00
|
|
|
struct net_connect_job
|
|
|
|
{
|
|
|
|
struct net_connection* con;
|
|
|
|
struct net_connect_handle* handle;
|
|
|
|
struct sockaddr_storage addr;
|
|
|
|
struct net_connect_job* next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct net_connect_handle
|
|
|
|
{
|
|
|
|
const char* address;
|
|
|
|
uint16_t port;
|
|
|
|
void* ptr;
|
|
|
|
net_connect_cb callback;
|
|
|
|
struct net_dns_job* dns;
|
|
|
|
const struct net_dns_result* result;
|
|
|
|
struct net_connect_job* job4;
|
|
|
|
struct net_connect_job* job6;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void net_connect_callback(struct net_connect_handle* handle, enum net_connect_status status, struct net_connection* con);
|
|
|
|
static void net_connect_job_internal_cb(struct net_connection* con, int event, void* ptr);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a connection job is completed.
|
|
|
|
* @return -1 on completed with an error, 0 on not yet completed, or 1 if completed successfully (connected).
|
|
|
|
*/
|
|
|
|
static int net_connect_job_check(struct net_connect_job* job)
|
|
|
|
{
|
|
|
|
struct net_connection* con = job->con;
|
|
|
|
int af = job->addr.ss_family;
|
|
|
|
enum net_connect_status status;
|
|
|
|
|
2013-09-06 19:17:43 +00:00
|
|
|
int ret = net_connect(net_con_get_sd(con), (struct sockaddr*) &job->addr, af == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
|
2013-02-05 21:40:20 +00:00
|
|
|
if (ret == 0 || (ret == -1 && net_error() == EISCONN))
|
|
|
|
{
|
|
|
|
LOG_TRACE("net_connect_job_check(): Socket connected!");
|
|
|
|
job->con = NULL;
|
2014-08-05 15:40:55 +00:00
|
|
|
net_con_clear_timeout(con);
|
2013-02-05 21:40:20 +00:00
|
|
|
net_connect_callback(job->handle, net_connect_status_ok, con);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else if (ret == -1 && (net_error() == EALREADY || net_error() == EINPROGRESS || net_error() == EWOULDBLOCK || net_error() == EINTR))
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
LOG_TRACE("net_connect_job_check(): Socket error!");
|
|
|
|
|
|
|
|
switch (net_error())
|
|
|
|
{
|
|
|
|
case ECONNREFUSED:
|
|
|
|
status = net_connect_status_refused;
|
|
|
|
break;
|
|
|
|
case ENETUNREACH:
|
|
|
|
status = net_connect_status_unreachable;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
status = net_connect_status_socket_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_connect_callback(job->handle, status, NULL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void net_connect_job_free(struct net_connect_job* job)
|
|
|
|
{
|
|
|
|
if (job->con)
|
|
|
|
net_con_close(job->con);
|
|
|
|
job->handle = NULL;
|
|
|
|
job->next = NULL;
|
|
|
|
hub_free(job);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void net_connect_job_stop(struct net_connect_job* job)
|
|
|
|
{
|
|
|
|
if (job->addr.ss_family == AF_INET6)
|
|
|
|
{
|
|
|
|
job->handle->job6 = job->next;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
job->handle->job4 = job->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_connect_job_free(job);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int net_connect_depleted(struct net_connect_handle* handle)
|
|
|
|
{
|
|
|
|
return (!handle->job6 && !handle->job4);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int net_connect_job_process(struct net_connect_job* job)
|
|
|
|
{
|
|
|
|
int sd;
|
|
|
|
if (!job->con)
|
|
|
|
{
|
|
|
|
sd = net_socket_create(job->addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
|
|
|
|
if (sd == -1)
|
|
|
|
{
|
|
|
|
LOG_DEBUG("net_connect_job_process: Unable to create socket!");
|
|
|
|
net_connect_callback(job->handle, net_connect_status_socket_error, NULL);
|
|
|
|
return -1; // FIXME
|
|
|
|
}
|
|
|
|
|
|
|
|
job->con = net_con_create();
|
|
|
|
net_con_initialize(job->con, sd, net_connect_job_internal_cb, job, NET_EVENT_WRITE);
|
|
|
|
net_con_set_timeout(job->con, TIMEOUT_CONNECTED); // FIXME: Use a proper timeout value!
|
|
|
|
}
|
|
|
|
|
|
|
|
return net_connect_job_check(job);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Internal callback used to establish an outbound connection.
|
|
|
|
*/
|
|
|
|
static void net_connect_job_internal_cb(struct net_connection* con, int event, void* ptr)
|
|
|
|
{
|
|
|
|
struct net_connect_job* job = net_con_get_ptr(con);
|
|
|
|
struct net_connect_job* next_job = job->next;
|
|
|
|
struct net_connect_handle* handle = job->handle;
|
|
|
|
|
|
|
|
if (event == NET_EVENT_TIMEOUT)
|
|
|
|
{
|
|
|
|
// FIXME: Try next address, or if no more addresses left declare failure to connect.
|
|
|
|
if (job->addr.ss_family == AF_INET6)
|
|
|
|
{
|
|
|
|
net_connect_job_stop(job);
|
|
|
|
|
|
|
|
if (!next_job)
|
|
|
|
{
|
|
|
|
LOG_TRACE("No more IPv6 addresses to try!");
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
net_connect_job_stop(job);
|
|
|
|
|
|
|
|
if (!next_job)
|
|
|
|
{
|
|
|
|
LOG_TRACE("No more IPv4 addresses to try!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (net_connect_depleted(handle))
|
|
|
|
{
|
|
|
|
LOG_TRACE("No more addresses left. Unable to connect!");
|
|
|
|
net_connect_callback(handle, net_connect_status_timeout, NULL);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event == NET_EVENT_WRITE)
|
|
|
|
{
|
|
|
|
net_connect_job_process(job);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-05-14 19:00:42 +00:00
|
|
|
static void net_connect_cancel(struct net_connect_handle* handle)
|
2013-02-05 21:40:20 +00:00
|
|
|
{
|
|
|
|
struct net_connect_job* job;
|
|
|
|
|
|
|
|
job = handle->job6;
|
|
|
|
while (job)
|
|
|
|
{
|
|
|
|
job = job->next;
|
|
|
|
net_connect_job_free(handle->job6);
|
|
|
|
handle->job6 = job;
|
|
|
|
}
|
|
|
|
|
|
|
|
job = handle->job4;
|
|
|
|
while (job)
|
|
|
|
{
|
|
|
|
job = job->next;
|
|
|
|
net_connect_job_free(handle->job4);
|
|
|
|
handle->job4 = job;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int net_connect_process_queue(struct net_connect_handle* handle, struct net_connect_job* job)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
while (job)
|
|
|
|
{
|
|
|
|
ret = net_connect_job_process(job);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
net_connect_job_stop(job);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (ret == 0)
|
|
|
|
{
|
|
|
|
// Need to process again
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// FIXME: Success!
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int net_connect_process(struct net_connect_handle* handle)
|
|
|
|
{
|
|
|
|
int ret4, ret6;
|
|
|
|
|
|
|
|
ret6 = net_connect_process_queue(handle, handle->job6);
|
|
|
|
if (ret6 == 1)
|
|
|
|
return 1; // Connected - cool!
|
|
|
|
|
|
|
|
net_connect_process_queue(handle, handle->job4);
|
2014-05-14 19:00:42 +00:00
|
|
|
return 0;
|
2013-02-05 21:40:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int net_connect_job_schedule(struct net_connect_handle* handle, struct ip_addr_encap* addr)
|
|
|
|
{
|
|
|
|
struct net_connect_job* job;
|
|
|
|
struct sockaddr_in* addr4;
|
|
|
|
struct sockaddr_in6* addr6;
|
|
|
|
|
|
|
|
if (addr->af == AF_INET6 && !net_is_ipv6_supported())
|
|
|
|
{
|
|
|
|
LOG_TRACE("net_connect_job_schedule(): Skipping IPv6 support since IPv6 is not supported.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
job = hub_malloc_zero(sizeof(struct net_connect_job));
|
|
|
|
job->handle = handle;
|
|
|
|
if (addr->af == AF_INET6)
|
|
|
|
{
|
|
|
|
addr6 = (struct sockaddr_in6*) &job->addr;
|
|
|
|
LOG_TRACE("net_connect_job_schedule(): Scheduling IPv6 connect job.");
|
|
|
|
addr6->sin6_family = AF_INET6;
|
|
|
|
addr6->sin6_port = htons(handle->port);
|
|
|
|
memcpy(&addr6->sin6_addr, &addr->internal_ip_data.in6, sizeof(struct in6_addr));
|
|
|
|
|
|
|
|
// prepend
|
|
|
|
job->next = handle->job6;
|
|
|
|
handle->job6 = job;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
addr4 = (struct sockaddr_in*) &job->addr;
|
|
|
|
LOG_TRACE("net_connect_job_schedule(): Scheduling IPv4 connect job.");
|
|
|
|
addr4->sin_family = AF_INET;
|
|
|
|
addr4->sin_port = htons(handle->port);
|
|
|
|
memcpy(&addr4->sin_addr, &addr->internal_ip_data.in, sizeof(struct in_addr));
|
|
|
|
|
|
|
|
// prepend
|
|
|
|
job->next = handle->job4;
|
|
|
|
handle->job4 = job;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Callback when the DNS results are ready.
|
|
|
|
* Create a list of IPv6 and IPv4 addresses, then
|
|
|
|
* start connecting to them one by one until one succeeds.
|
|
|
|
*/
|
|
|
|
static int net_con_connect_dns_callback(struct net_dns_job* job, const struct net_dns_result* result)
|
|
|
|
{
|
|
|
|
struct ip_addr_encap* addr;
|
|
|
|
struct net_connect_handle* handle = (struct net_connect_handle*) net_dns_job_get_ptr(job);
|
|
|
|
handle->dns = NULL;
|
|
|
|
size_t usable = 0;
|
|
|
|
|
|
|
|
LOG_TRACE("net_con_connect(): async - Got DNS results");
|
|
|
|
if (!result)
|
|
|
|
{
|
|
|
|
LOG_DEBUG("net_con_connect() - Unable to lookup host!");
|
|
|
|
net_connect_callback(handle, net_connect_status_dns_error, NULL);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!net_dns_result_size(result))
|
|
|
|
{
|
|
|
|
LOG_DEBUG("net_con_connect() - Host not found!");
|
|
|
|
net_connect_callback(handle, net_connect_status_host_not_found, NULL);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
handle->result = result;
|
|
|
|
|
|
|
|
// Extract results into a separate list of IPv4 and IPv6 addresses.
|
|
|
|
addr = net_dns_result_first(result);
|
|
|
|
while (addr)
|
|
|
|
{
|
|
|
|
if (net_connect_job_schedule(handle, addr))
|
|
|
|
usable++;
|
|
|
|
addr = net_dns_result_next(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
net_connect_process(handle);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// typedef void (*net_connect_cb)(struct net_connect_handle*, enum net_connect_handle_code, struct net_connection* con);
|
|
|
|
|
|
|
|
struct net_connect_handle* net_con_connect(const char* address, uint16_t port, net_connect_cb callback, void* ptr)
|
|
|
|
{
|
|
|
|
struct net_connect_handle* handle = hub_malloc_zero(sizeof(struct net_connect_handle));
|
|
|
|
|
|
|
|
handle->address = hub_strdup(address);
|
|
|
|
handle->port = port;
|
|
|
|
handle->ptr = ptr;
|
|
|
|
handle->callback = callback;
|
|
|
|
|
|
|
|
// FIXME: Check if DNS resolving is necessary ?
|
|
|
|
handle->dns = net_dns_gethostbyname(address, AF_UNSPEC, net_con_connect_dns_callback, handle);
|
|
|
|
if (!handle->dns)
|
|
|
|
{
|
|
|
|
LOG_TRACE("net_con_connect(): Unable to create DNS lookup job.");
|
|
|
|
hub_free((char*) handle->address);
|
|
|
|
hub_free(handle);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_connect_destroy(struct net_connect_handle* handle)
|
|
|
|
{
|
|
|
|
hub_free((char*) handle->address);
|
|
|
|
|
|
|
|
// cancel DNS job if pending
|
|
|
|
if (handle->dns)
|
|
|
|
net_dns_job_cancel(handle->dns);
|
|
|
|
|
|
|
|
// Stop any connect jobs.
|
|
|
|
net_connect_cancel(handle);
|
|
|
|
|
|
|
|
// free any DNS results
|
|
|
|
net_dns_result_free(handle->result);
|
|
|
|
|
|
|
|
hub_free(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void net_connect_callback(struct net_connect_handle* handle, enum net_connect_status status, struct net_connection* con)
|
|
|
|
{
|
|
|
|
uhub_assert(handle->callback != NULL);
|
|
|
|
|
|
|
|
// Call the callback
|
|
|
|
handle->callback(handle, status, con, handle->ptr);
|
|
|
|
handle->callback = NULL;
|
|
|
|
|
|
|
|
// Cleanup
|
|
|
|
net_connect_destroy(handle);
|
|
|
|
}
|
2014-10-16 21:09:12 +00:00
|
|
|
|
|
|
|
static void timeout_callback(struct timeout_evt* evt)
|
|
|
|
{
|
|
|
|
net_con_callback((struct net_connection*) evt->ptr, NET_EVENT_TIMEOUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void net_con_set_timeout(struct net_connection* con, int seconds)
|
|
|
|
{
|
|
|
|
if (!con->timeout)
|
|
|
|
{
|
|
|
|
con->timeout = hub_malloc_zero(sizeof(struct timeout_evt));
|
|
|
|
timeout_evt_initialize(con->timeout, timeout_callback, con);
|
|
|
|
timeout_queue_insert(net_backend_get_timeout_queue(), con->timeout, seconds);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
timeout_queue_reschedule(net_backend_get_timeout_queue(), con->timeout, seconds);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_con_clear_timeout(struct net_connection* con)
|
|
|
|
{
|
|
|
|
if (con->timeout && timeout_evt_is_scheduled(con->timeout))
|
|
|
|
{
|
|
|
|
timeout_queue_remove(net_backend_get_timeout_queue(), con->timeout);
|
|
|
|
hub_free(con->timeout);
|
|
|
|
con->timeout = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|