aboutsummaryrefslogtreecommitdiff
path: root/libsylph/socket.c
diff options
context:
space:
mode:
authorhiro <hiro@ee746299-78ed-0310-b773-934348b2243d>2012-05-31 09:48:17 +0000
committerhiro <hiro@ee746299-78ed-0310-b773-934348b2243d>2012-05-31 09:48:17 +0000
commite2af0838c3e99517f3f611f46e70d2a8e9560d7f (patch)
tree077d938d6a8fdbd575ea7fd2284af3133dc7fcc0 /libsylph/socket.c
parent23551f3f4289951a1ca6093eab152700dd0e9d21 (diff)
differentiate DNS lookup error from connection errors.
git-svn-id: svn://sylpheed.sraoss.jp/sylpheed/trunk@3081 ee746299-78ed-0310-b773-934348b2243d
Diffstat (limited to 'libsylph/socket.c')
-rw-r--r--libsylph/socket.c222
1 files changed, 169 insertions, 53 deletions
diff --git a/libsylph/socket.c b/libsylph/socket.c
index 6005f3cf..52a3fb63 100644
--- a/libsylph/socket.c
+++ b/libsylph/socket.c
@@ -91,8 +91,8 @@ struct _SockConnectData {
#if USE_THREADS
gint flag;
GThread *thread;
- SockInfo *sock;
#endif /* G_OS_UNIX */
+ SockInfo *sock;
SockConnectFunc func;
gpointer data;
};
@@ -146,9 +146,8 @@ static gint sock_connect_with_timeout (gint sock,
guint timeout_secs);
#ifndef INET6
-static gint sock_connect_by_hostname (gint sock,
- const gchar *hostname,
- gushort port);
+static gint sock_info_connect_by_hostname
+ (SockInfo *sock);
#else
#ifdef G_OS_WIN32
typedef int (*GetAddrInfoFunc) (const char *node,
@@ -166,8 +165,7 @@ static FreeAddrInfoFunc freeaddrinfo_func = NULL;
#define freeaddrinfo my_freeaddrinfo
#endif
-static SockDesc sock_connect_by_getaddrinfo (const gchar *hostname,
- gushort port);
+static SockDesc sock_info_connect_by_getaddrinfo(SockInfo *sock);
#endif
#ifdef G_OS_UNIX
@@ -377,6 +375,22 @@ gint fd_accept(gint sock)
}
+SockInfo *sock_new(const gchar *hostname, gushort port)
+{
+ SockInfo *sockinfo;
+
+ sockinfo = g_new0(SockInfo, 1);
+ sockinfo->sock = INVALID_SOCKET;
+ sockinfo->sock_ch = NULL;
+ sockinfo->hostname = g_strdup(hostname);
+ sockinfo->port = port;
+ sockinfo->state = CONN_READY;
+ sockinfo->flags = 0;
+ sockinfo->data = NULL;
+
+ return sockinfo;
+}
+
static SockInfo *sock_find_from_fd(gint fd)
{
GList *cur;
@@ -816,11 +830,14 @@ static gint my_inet_aton(const gchar *hostname, struct in_addr *inp)
#endif /* !defined(INET6) || defined(G_OS_WIN32) */
#ifndef INET6
-static gint sock_connect_by_hostname(gint sock, const gchar *hostname,
- gushort port)
+static gint sock_info_connect_by_hostname(SockInfo *sock)
{
struct hostent *hp;
struct sockaddr_in ad;
+ gint ret;
+
+ g_return_val_if_fail(sock != NULL, -1);
+ g_return_val_if_fail(sock->hostname != NULL && sock->port > 0, -1);
resolver_init();
@@ -828,24 +845,32 @@ static gint sock_connect_by_hostname(gint sock, const gchar *hostname,
ad.sin_family = AF_INET;
ad.sin_port = htons(port);
- if (!my_inet_aton(hostname, &ad.sin_addr)) {
- if ((hp = my_gethostbyname(hostname)) == NULL) {
- fprintf(stderr, "%s: unknown host.\n", hostname);
+ if (!my_inet_aton(sock->hostname, &ad.sin_addr)) {
+ if ((hp = my_gethostbyname(sock->hostname)) == NULL) {
+ fprintf(stderr, "%s: unknown host.\n", sock->hostname);
errno = 0;
+ sock->state = CONN_LOOKUPFAILED;
return -1;
}
if (hp->h_length != 4 && hp->h_length != 8) {
- fprintf(stderr, "illegal address length received for host %s\n", hostname);
+ fprintf(stderr, "illegal address length received for host %s\n", sock->hostname);
errno = 0;
+ sock->state = CONN_LOOKUPFAILED;
return -1;
}
memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
}
- return sock_connect_with_timeout(sock, (struct sockaddr *)&ad,
- sizeof(ad), io_timeout);
+ sock->state = CONN_LOOKUPSUCCESS;
+
+ ret = sock_connect_with_timeout(sock->sock, (struct sockaddr *)&ad,
+ sizeof(ad), io_timeout);
+ if (ret < 0)
+ sock->state = CONN_FAILED;
+ else
+ sock->state = CONN_ESTABLISHED;
}
#else /* INET6 */
@@ -982,13 +1007,16 @@ const gchar *gai_strerror(gint errcode)
}
#endif
-static SockDesc sock_connect_by_getaddrinfo(const gchar *hostname, gushort port)
+static SockDesc sock_info_connect_by_getaddrinfo(SockInfo *sockinfo)
{
SockDesc sock = INVALID_SOCKET;
gint gai_error;
struct addrinfo hints, *res, *ai;
gchar port_str[6];
+ g_return_val_if_fail(sockinfo != NULL, INVALID_SOCKET);
+ g_return_val_if_fail(sockinfo->hostname != NULL && sockinfo->port > 0, INVALID_SOCKET);
+
resolver_init();
memset(&hints, 0, sizeof(hints));
@@ -998,11 +1026,12 @@ static SockDesc sock_connect_by_getaddrinfo(const gchar *hostname, gushort port)
hints.ai_protocol = IPPROTO_TCP;
/* convert port from integer to string. */
- g_snprintf(port_str, sizeof(port_str), "%d", port);
+ g_snprintf(port_str, sizeof(port_str), "%d", sockinfo->port);
- if ((gai_error = getaddrinfo(hostname, port_str, &hints, &res)) != 0) {
+ if ((gai_error = getaddrinfo(sockinfo->hostname, port_str, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo for %s:%s failed: %s\n",
- hostname, port_str, gai_strerror(gai_error));
+ sockinfo->hostname, port_str, gai_strerror(gai_error));
+ sockinfo->state = CONN_LOOKUPFAILED;
return INVALID_SOCKET;
}
@@ -1022,22 +1051,45 @@ static SockDesc sock_connect_by_getaddrinfo(const gchar *hostname, gushort port)
if (res != NULL)
freeaddrinfo(res);
- if (ai == NULL)
+ if (ai == NULL) {
+ sockinfo->state = CONN_FAILED;
return INVALID_SOCKET;
+ }
+ sockinfo->state = CONN_ESTABLISHED;
return sock;
}
#endif /* !INET6 */
SockInfo *sock_connect(const gchar *hostname, gushort port)
{
- SockDesc sock;
SockInfo *sockinfo;
-#ifdef INET6
- sock = sock_connect_by_getaddrinfo(hostname, port);
- if (!SOCKET_IS_VALID(sock))
+ sockinfo = sock_new(hostname, port);
+ if (sock_info_connect(sockinfo) < 0) {
+ sock_close(sockinfo);
return NULL;
+ }
+
+ return sockinfo;
+}
+
+gint sock_info_connect(SockInfo *sockinfo)
+{
+ SockDesc sock;
+#ifndef INET6
+ gint ret;
+#endif
+
+ g_return_val_if_fail(sockinfo != NULL, -1);
+ g_return_val_if_fail(sockinfo->hostname != NULL && sockinfo->port > 0,
+ -1);
+
+#ifdef INET6
+ sock = sock_info_connect_by_getaddrinfo(sockinfo);
+ if (!SOCKET_IS_VALID(sock)) {
+ return -1;
+ }
#else
sock = socket(AF_INET, SOCK_STREAM, 0);
if (!SOCKET_IS_VALID(sock)) {
@@ -1046,30 +1098,29 @@ SockInfo *sock_connect(const gchar *hostname, gushort port)
#else
perror("socket");
#endif /* G_OS_WIN32 */
- return NULL;
+ sockinfo->state = CONN_FAILED;
+ return -1;
}
sock_set_buffer_size(sock);
- if (sock_connect_by_hostname(sock, hostname, port) < 0) {
+ sockinfo->sock = sock;
+ if ((ret = sock_info_connect_by_hostname(sockinfo)) < 0) {
if (errno != 0) perror("connect");
fd_close(sock);
- return NULL;
+ sockinfo->sock = INVALID_SOCKET;
+ return ret;
}
#endif /* INET6 */
- sockinfo = g_new0(SockInfo, 1);
sockinfo->sock = sock;
sockinfo->sock_ch = g_io_channel_unix_new(sock);
- sockinfo->hostname = g_strdup(hostname);
- sockinfo->port = port;
- sockinfo->state = CONN_ESTABLISHED;
sockinfo->flags = SYL_SOCK_CHECK_IO;
sock_list = g_list_prepend(sock_list, sockinfo);
g_usleep(100000);
- return sockinfo;
+ return 0;
}
#ifdef G_OS_UNIX
@@ -1126,11 +1177,9 @@ static gboolean sock_connect_async_cb(GIOChannel *source,
return FALSE;
}
- sockinfo = g_new0(SockInfo, 1);
+ sockinfo = conn_data->sock;
sockinfo->sock = fd;
sockinfo->sock_ch = g_io_channel_unix_new(fd);
- sockinfo->hostname = g_strdup(conn_data->hostname);
- sockinfo->port = conn_data->port;
sockinfo->state = CONN_ESTABLISHED;
sockinfo->flags = SYL_SOCK_NONBLOCK;
@@ -1138,6 +1187,7 @@ static gboolean sock_connect_async_cb(GIOChannel *source,
conn_data->func(sockinfo, conn_data->data);
+ conn_data->sock = NULL;
sock_connect_async_cancel(conn_data->id);
return FALSE;
@@ -1158,22 +1208,40 @@ static gint sock_connect_async_get_address_info_cb(GList *addr_list,
gint sock_connect_async(const gchar *hostname, gushort port,
SockConnectFunc func, gpointer data)
{
+ SockInfo *sock;
+ gint ret;
+
+ sock = sock_new(hostname, port);
+ ret = sock_info_connect_async(sock, func, data);
+ if (ret < 0)
+ sock_close(sock);
+
+ return ret;
+}
+
+gint sock_info_connect_async(SockInfo *sock, SockConnectFunc func,
+ gpointer data)
+{
static gint id = 1;
SockConnectData *conn_data;
+ g_return_val_if_fail(sock != NULL, -1);
+ g_return_val_if_fail(sock->hostname != NULL && sock->port > 0, -1);
+
conn_data = g_new0(SockConnectData, 1);
conn_data->id = id++;
- conn_data->hostname = g_strdup(hostname);
- conn_data->port = port;
+ conn_data->hostname = g_strdup(sock->hostname);
+ conn_data->port = sock->port;
conn_data->addr_list = NULL;
conn_data->cur_addr = NULL;
conn_data->io_tag = 0;
+ conn_data->sock = sock;
conn_data->func = func;
conn_data->data = data;
conn_data->lookup_data = sock_get_address_info_async
- (hostname, port, sock_connect_async_get_address_info_cb,
- conn_data);
+ (sock->hostname, sock->port,
+ sock_connect_async_get_address_info_cb, conn_data);
if (conn_data->lookup_data == NULL) {
g_free(conn_data->hostname);
@@ -1213,6 +1281,8 @@ gint sock_connect_async_cancel(gint id)
g_io_channel_shutdown(conn_data->channel, FALSE, NULL);
g_io_channel_unref(conn_data->channel);
}
+ if (conn_data->sock)
+ sock_close(conn_data->sock);
sock_address_list_free(conn_data->addr_list);
g_free(conn_data->hostname);
@@ -1230,6 +1300,15 @@ static gint sock_connect_address_list_async(SockConnectData *conn_data)
SockAddrData *addr_data;
gint sock = -1;
+ if (conn_data->addr_list == NULL) {
+ g_warning("sock_connect_address_list_async: "
+ "DNS lookup for %s failed", conn_data->hostname);
+ conn_data->sock->state = CONN_LOOKUPFAILED;
+ conn_data->func(conn_data->sock, conn_data->data);
+ sock_connect_async_cancel(conn_data->id);
+ return -1;
+ }
+
for (; conn_data->cur_addr != NULL;
conn_data->cur_addr = conn_data->cur_addr->next) {
addr_data = (SockAddrData *)conn_data->cur_addr->data;
@@ -1256,9 +1335,10 @@ static gint sock_connect_address_list_async(SockConnectData *conn_data)
if (conn_data->cur_addr == NULL) {
g_warning("sock_connect_address_list_async: "
- "connection to %s:%d failed\n",
+ "connection to %s:%d failed",
conn_data->hostname, conn_data->port);
- conn_data->func(NULL, conn_data->data);
+ conn_data->sock->state = CONN_FAILED;
+ conn_data->func(conn_data->sock, conn_data->data);
sock_connect_async_cancel(conn_data->id);
return -1;
}
@@ -1319,7 +1399,7 @@ static gboolean sock_get_address_info_async_cb(GIOChannel *source,
break;
if (ai_member[0] == AF_UNSPEC) {
- g_warning("DNS lookup failed\n");
+ g_warning("DNS lookup failed");
break;
}
@@ -1412,7 +1492,7 @@ static SockLookupData *sock_get_address_info_async(const gchar *hostname,
gai_err = getaddrinfo(hostname, port_str, &hints, &res);
if (gai_err != 0) {
- g_warning("getaddrinfo for %s:%s failed: %s\n",
+ g_warning("getaddrinfo for %s:%s failed: %s",
hostname, port_str, gai_strerror(gai_err));
fd_write_all(pipe_fds[1], (gchar *)ai_member,
sizeof(ai_member));
@@ -1507,14 +1587,15 @@ static gpointer sock_connect_async_func(gpointer data)
SockConnectData *conn_data = (SockConnectData *)data;
gint ret;
- conn_data->sock = sock_connect(conn_data->hostname, conn_data->port);
+ ret = sock_info_connect(conn_data->sock);
- if (conn_data->sock) {
+ if (ret == 0) {
debug_print("sock_connect_async_func: connected\n");
- ret = 0;
} else {
- debug_print("sock_connect_async_func: connection failed\n");
- ret = -1;
+ if (conn_data->sock->state == CONN_LOOKUPFAILED)
+ debug_print("sock_connect_async_func: DNS lookup failed\n");
+ else
+ debug_print("sock_connect_async_func: connection failed\n");
}
g_atomic_int_set(&conn_data->flag, 1);
@@ -1526,15 +1607,31 @@ static gpointer sock_connect_async_func(gpointer data)
gint sock_connect_async_thread(const gchar *hostname, gushort port)
{
+ SockInfo *sock;
+ gint ret;
+
+ sock = sock_new(hostname, port);
+ ret = sock_info_connect_async_thread(sock);
+ if (ret < 0)
+ sock_close(sock);
+
+ return ret;
+}
+
+gint sock_info_connect_async_thread(SockInfo *sock)
+{
static gint id = 1;
SockConnectData *data;
+ g_return_val_if_fail(sock != NULL, -1);
+ g_return_val_if_fail(sock->hostname != NULL && sock->port > 0, -1);
+
data = g_new0(SockConnectData, 1);
data->id = id++;
- data->hostname = g_strdup(hostname);
- data->port = port;
+ data->hostname = g_strdup(sock->hostname);
+ data->port = sock->port;
data->flag = 0;
- data->sock = NULL;
+ data->sock = sock;
data->thread = g_thread_create(sock_connect_async_func, data, TRUE,
NULL);
@@ -1551,6 +1648,22 @@ gint sock_connect_async_thread(const gchar *hostname, gushort port)
gint sock_connect_async_thread_wait(gint id, SockInfo **sock)
{
+ gint ret;
+
+ *sock = NULL;
+ ret = sock_info_connect_async_thread_wait(id, sock);
+ if (ret < 0) {
+ if (*sock) {
+ sock_close(*sock);
+ *sock = NULL;
+ }
+ }
+
+ return ret;
+}
+
+gint sock_info_connect_async_thread_wait(gint id, SockInfo **sock)
+{
SockConnectData *conn_data = NULL;
GList *cur;
gint ret;
@@ -1563,7 +1676,7 @@ gint sock_connect_async_thread_wait(gint id, SockInfo **sock)
}
if (!conn_data) {
- g_warning("sock_connect_async_thread_wait: id %d not found.", id);
+ g_warning("sock_info_connect_async_thread_wait: id %d not found.", id);
return -1;
}
@@ -1572,9 +1685,10 @@ gint sock_connect_async_thread_wait(gint id, SockInfo **sock)
event_loop_iterate();
ret = GPOINTER_TO_INT(g_thread_join(conn_data->thread));
- debug_print("sock_connect_async_thread_wait: thread exited with status %d\n", ret);
+ debug_print("sock_info_connect_async_thread_wait: thread exited with status %d\n", ret);
- *sock = conn_data->sock;
+ if (sock)
+ *sock = conn_data->sock;
sock_connect_data_list = g_list_remove(sock_connect_data_list,
conn_data);
@@ -1973,6 +2087,8 @@ gint sock_close(SockInfo *sock)
if (!sock)
return 0;
+ debug_print("sock_close: %s:%u (%p)\n", sock->hostname ? sock->hostname : "(none)", sock->port, sock);
+
#if USE_SSL
if (sock->ssl)
ssl_done_socket(sock);