From e2af0838c3e99517f3f611f46e70d2a8e9560d7f Mon Sep 17 00:00:00 2001 From: hiro Date: Thu, 31 May 2012 09:48:17 +0000 Subject: differentiate DNS lookup error from connection errors. git-svn-id: svn://sylpheed.sraoss.jp/sylpheed/trunk@3081 ee746299-78ed-0310-b773-934348b2243d --- libsylph/imap.c | 1 + libsylph/nntp.c | 1 + libsylph/session.c | 122 ++++++++++++++++++++++++----- libsylph/session.h | 12 +++ libsylph/socket.c | 222 ++++++++++++++++++++++++++++++++++++++++------------- libsylph/socket.h | 12 +++ 6 files changed, 296 insertions(+), 74 deletions(-) (limited to 'libsylph') diff --git a/libsylph/imap.c b/libsylph/imap.c index fee09646..d9c98857 100644 --- a/libsylph/imap.c +++ b/libsylph/imap.c @@ -2960,6 +2960,7 @@ static SockInfo *imap_open(const gchar *server, gushort port, if (socks_connect(sock, server, port, socks_info) < 0) { log_warning("Can't establish SOCKS connection: %s:%d\n", server, port); + sock_close(sock); return NULL; } } diff --git a/libsylph/nntp.c b/libsylph/nntp.c index daed4ff1..64c9da12 100644 --- a/libsylph/nntp.c +++ b/libsylph/nntp.c @@ -87,6 +87,7 @@ Session *nntp_session_new_full(const gchar *server, gushort port, if (socks_connect(sock, server, port, socks_info) < 0) { log_warning("Can't establish SOCKS connection: %s:%d\n", server, port); + sock_close(sock); return NULL; } } diff --git a/libsylph/session.c b/libsylph/session.c index 31525d8b..9f150230 100644 --- a/libsylph/session.c +++ b/libsylph/session.c @@ -40,6 +40,7 @@ typedef struct _SessionPrivData SessionPrivData; struct _SessionPrivData { Session *session; SocksInfo *socks_info; + SessionErrorValue error_val; gpointer data; }; @@ -83,6 +84,8 @@ static gboolean session_write_data_cb (SockInfo *source, void session_init(Session *session) { + SessionPrivData *priv; + session->type = SESSION_UNKNOWN; session->sock = NULL; session->server = NULL; @@ -127,6 +130,12 @@ void session_init(Session *session) session->ping_tag = 0; session->data = NULL; + + priv = g_new0(SessionPrivData, 1); + priv->session = session; + priv->socks_info = NULL; + priv->error_val = SESSION_ERROR_OK; + priv_list = g_list_prepend(priv_list, priv); } static SessionPrivData *session_get_priv(Session *session) @@ -153,6 +162,7 @@ gint session_connect(Session *session, const gchar *server, gushort port) gint session_connect_full(Session *session, const gchar *server, gushort port, SocksInfo *socks_info) { + SessionPrivData *priv; #ifndef G_OS_UNIX SockInfo *sock = NULL; #endif @@ -160,6 +170,10 @@ gint session_connect_full(Session *session, const gchar *server, gushort port, g_return_val_if_fail(server != NULL, -1); g_return_val_if_fail(port > 0, -1); + priv = session_get_priv(session); + g_return_val_if_fail(priv != NULL, -1); + priv->socks_info = socks_info; + if (session->server != server) { g_free(session->server); session->server = g_strdup(server); @@ -178,6 +192,7 @@ gint session_connect_full(Session *session, const gchar *server, gushort port, if (session->conn_id < 0) { g_warning("can't connect to server."); session->state = SESSION_ERROR; + priv->error_val = SESSION_ERROR_CONNFAIL; return -1; } #elif USE_THREADS @@ -185,31 +200,24 @@ gint session_connect_full(Session *session, const gchar *server, gushort port, if (session->conn_id < 0) { g_warning("can't connect to server."); session->state = SESSION_ERROR; + priv->error_val = SESSION_ERROR_CONNFAIL; return -1; } - if (sock_connect_async_thread_wait(session->conn_id, &sock) < 0) { - g_warning("can't connect to server."); - session->state = SESSION_ERROR; + if (sock_info_connect_async_thread_wait(session->conn_id, &sock) < 0) { + session_connect_cb(sock, session); + if (sock) + sock_close(sock); return -1; } #else /* !USE_THREADS */ - sock = sock_connect(server, port); - if (sock == NULL) { - g_warning("can't connect to server."); - session->state = SESSION_ERROR; + sock = sock_new(server, port); + if (sock_info_connect(sock) < 0) { + session_connect_cb(sock, session); + sock_close(sock); return -1; } #endif - if (socks_info) { - SessionPrivData *priv; - - priv = g_new0(SessionPrivData, 1); - priv->session = session; - priv->socks_info = socks_info; - priv_list = g_list_prepend(priv_list, priv); - } - #ifdef G_OS_UNIX return 0; #else @@ -222,23 +230,37 @@ static gint session_connect_cb(SockInfo *sock, gpointer data) Session *session = SESSION(data); SessionPrivData *priv; + priv = session_get_priv(session); session->conn_id = 0; if (!sock) { g_warning("can't connect to server."); session->state = SESSION_ERROR; + priv->error_val = SESSION_ERROR_CONNFAIL; + return -1; + } + if (sock->state == CONN_LOOKUPFAILED) { + g_warning("DNS lookup failed."); + session->state = SESSION_ERROR; + priv->error_val = SESSION_ERROR_LOOKUP; + return -1; + } else if (sock->state != CONN_ESTABLISHED) { + g_warning("can't connect to server (ConnectionState: %d).", + sock->state); + session->state = SESSION_ERROR; + priv->error_val = SESSION_ERROR_CONNFAIL; return -1; } session->sock = sock; - priv = session_get_priv(session); - if (priv && priv->socks_info) { + if (priv->socks_info) { sock_set_nonblocking_mode(sock, FALSE); if (socks_connect(sock, session->server, session->port, priv->socks_info) < 0) { g_warning("can't establish SOCKS connection."); session->state = SESSION_ERROR; + priv->error_val = SESSION_ERROR_CONNFAIL; return -1; } } @@ -249,6 +271,7 @@ static gint session_connect_cb(SockInfo *sock, gpointer data) if (!ssl_init_socket(sock)) { g_warning("can't initialize SSL."); session->state = SESSION_ERROR; + priv->error_val = SESSION_ERROR_SOCKET; return -1; } } @@ -259,6 +282,7 @@ static gint session_connect_cb(SockInfo *sock, gpointer data) sock_set_nonblocking_mode(sock, session->nonblocking); session->state = SESSION_RECV; + priv->error_val = SESSION_ERROR_OK; session->io_tag = sock_add_watch(session->sock, G_IO_IN, session_read_msg_cb, session); @@ -312,6 +336,17 @@ gboolean session_is_connected(Session *session) session->state == SESSION_RECV); } +SessionErrorValue session_get_error(Session *session) +{ + SessionPrivData *priv; + + priv = session_get_priv(session); + if (priv) + return priv->error_val; + else + return SESSION_ERROR_ERROR; +} + void session_set_access_time(Session *session) { session->last_access_time = time(NULL); @@ -333,6 +368,7 @@ void session_set_timeout(Session *session, guint interval) static gboolean session_timeout_cb(gpointer data) { Session *session = SESSION(data); + SessionPrivData *priv; g_warning("session timeout.\n"); @@ -343,6 +379,8 @@ static gboolean session_timeout_cb(gpointer data) session->timeout_tag = 0; session->state = SESSION_TIMEOUT; + priv = session_get_priv(session); + priv->error_val = SESSION_ERROR_TIMEOUT; return FALSE; } @@ -681,6 +719,7 @@ static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition, gpointer data) { Session *session = SESSION(data); + SessionPrivData *priv; gchar buf[SESSION_BUFFSIZE]; gint line_len; gchar *newline; @@ -708,6 +747,8 @@ static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition, default: g_warning("%s: sock_read: %s\n", G_STRFUNC, g_strerror(errno)); session->state = SESSION_ERROR; + priv = session_get_priv(session); + priv->error_val = SESSION_ERROR_SOCKET; return FALSE; } } @@ -759,8 +800,11 @@ static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition, g_free(msg); - if (ret < 0) + if (ret < 0) { session->state = SESSION_ERROR; + priv = session_get_priv(session); + priv->error_val = SESSION_ERROR_SOCKET; + } return FALSE; } @@ -769,6 +813,7 @@ static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition, gpointer data) { Session *session = SESSION(data); + SessionPrivData *priv; GByteArray *data_buf; gint terminator_len; gboolean complete = FALSE; @@ -796,6 +841,8 @@ static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition, default: g_warning("%s: sock_read: %s\n", G_STRFUNC, g_strerror(errno)); session->state = SESSION_ERROR; + priv = session_get_priv(session); + priv->error_val = SESSION_ERROR_SOCKET; return FALSE; } } @@ -866,8 +913,11 @@ static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition, session->recv_data_notify(session, data_len, session->recv_data_notify_data); - if (ret < 0) + if (ret < 0) { session->state = SESSION_ERROR; + priv = session_get_priv(session); + priv->error_val = SESSION_ERROR_SOCKET; + } return FALSE; } @@ -882,6 +932,7 @@ static gboolean session_read_data_as_file_cb(SockInfo *source, gpointer data) { Session *session = SESSION(data); + SessionPrivData *priv; gint terminator_len; gchar *data_begin_p; gint buf_data_len; @@ -909,6 +960,8 @@ static gboolean session_read_data_as_file_cb(SockInfo *source, default: g_warning("%s: sock_read: %s\n", G_STRFUNC, g_strerror(errno)); session->state = SESSION_ERROR; + priv = session_get_priv(session); + priv->error_val = SESSION_ERROR_SOCKET; return FALSE; } } @@ -980,6 +1033,8 @@ static gboolean session_read_data_as_file_cb(SockInfo *source, g_warning("session_read_data_as_file_cb: " "writing data to file failed\n"); session->state = SESSION_ERROR; + priv = session_get_priv(session); + priv->error_val = SESSION_ERROR_IO; return FALSE; } session->read_data_pos += write_len; @@ -1016,6 +1071,8 @@ static gboolean session_read_data_as_file_cb(SockInfo *source, g_warning("session_read_data_as_file_cb: " "writing data to file failed\n"); session->state = SESSION_ERROR; + priv = session_get_priv(session); + priv->error_val = SESSION_ERROR_IO; return FALSE; } session->read_data_pos += write_len; @@ -1025,6 +1082,8 @@ static gboolean session_read_data_as_file_cb(SockInfo *source, g_warning("session_read_data_as_file_cb: " "writing data to file failed\n"); session->state = SESSION_ERROR; + priv = session_get_priv(session); + priv->error_val = SESSION_ERROR_IO; return FALSE; } rewind(session->read_data_fp); @@ -1046,8 +1105,11 @@ static gboolean session_read_data_as_file_cb(SockInfo *source, session->read_data_pos = 0; - if (ret < 0) + if (ret < 0) { session->state = SESSION_ERROR; + priv = session_get_priv(session); + priv->error_val = SESSION_ERROR_IO; + } return FALSE; } @@ -1056,6 +1118,7 @@ static gint session_write_buf(Session *session) { gint write_len; gint to_write_len; + SessionPrivData *priv; g_return_val_if_fail(session->write_buf != NULL, -1); g_return_val_if_fail(session->write_buf_p != NULL, -1); @@ -1076,6 +1139,8 @@ static gint session_write_buf(Session *session) default: g_warning("sock_write: %s\n", g_strerror(errno)); session->state = SESSION_ERROR; + priv = session_get_priv(session); + priv->error_val = SESSION_ERROR_SOCKET; return -1; } } @@ -1102,6 +1167,7 @@ static gint session_write_data(Session *session, gint *nwritten) gchar buf[WRITE_DATA_BUFFSIZE]; gint write_len; gint to_write_len; + SessionPrivData *priv; g_return_val_if_fail(session->write_data_fp != NULL, -1); g_return_val_if_fail(session->write_data_pos >= 0, -1); @@ -1112,6 +1178,8 @@ static gint session_write_data(Session *session, gint *nwritten) if (fread(buf, to_write_len, 1, session->write_data_fp) < 1) { g_warning("session_write_data: reading data from file failed\n"); session->state = SESSION_ERROR; + priv = session_get_priv(session); + priv->error_val = SESSION_ERROR_IO; return -1; } @@ -1125,6 +1193,8 @@ static gint session_write_data(Session *session, gint *nwritten) default: g_warning("sock_write: %s\n", g_strerror(errno)); session->state = SESSION_ERROR; + priv = session_get_priv(session); + priv->error_val = SESSION_ERROR_SOCKET; *nwritten = write_len; return -1; } @@ -1140,6 +1210,8 @@ static gint session_write_data(Session *session, gint *nwritten) session->write_data_pos, SEEK_SET) < 0) { g_warning("session_write_data: file seek failed\n"); session->state = SESSION_ERROR; + priv = session_get_priv(session); + priv->error_val = SESSION_ERROR_IO; return -1; } } @@ -1157,6 +1229,7 @@ static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition, gpointer data) { Session *session = SESSION(data); + SessionPrivData *priv; gint ret; g_return_val_if_fail(condition == G_IO_OUT, FALSE); @@ -1168,6 +1241,9 @@ static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition, if (ret < 0) { session->state = SESSION_ERROR; + priv = session_get_priv(session); + if (priv->error_val == SESSION_ERROR_OK) + priv->error_val = SESSION_ERROR_IO; return FALSE; } else if (ret > 0) return TRUE; @@ -1186,6 +1262,7 @@ static gboolean session_write_data_cb(SockInfo *source, GIOCondition condition, gpointer data) { Session *session = SESSION(data); + SessionPrivData *priv; guint write_data_len; gint write_len; gint ret; @@ -1201,6 +1278,9 @@ static gboolean session_write_data_cb(SockInfo *source, if (ret < 0) { session->state = SESSION_ERROR; + priv = session_get_priv(session); + if (priv->error_val == SESSION_ERROR_OK) + priv->error_val = SESSION_ERROR_IO; return FALSE; } else if (ret > 0) { GTimeVal tv_cur; diff --git a/libsylph/session.h b/libsylph/session.h index a807a4ae..d41d169c 100644 --- a/libsylph/session.h +++ b/libsylph/session.h @@ -73,6 +73,16 @@ typedef enum } SSLType; #endif +typedef enum { + SESSION_ERROR_OK, + SESSION_ERROR_LOOKUP, + SESSION_ERROR_CONNFAIL, + SESSION_ERROR_IO, + SESSION_ERROR_SOCKET, + SESSION_ERROR_TIMEOUT, + SESSION_ERROR_ERROR +} SessionErrorValue; + typedef gint (*RecvMsgNotify) (Session *session, const gchar *msg, gpointer user_data); @@ -190,6 +200,8 @@ gint session_disconnect (Session *session); void session_destroy (Session *session); gboolean session_is_connected (Session *session); +SessionErrorValue session_get_error (Session *session); + void session_set_access_time (Session *session); void session_set_timeout (Session *session, 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; @@ -1157,23 +1207,41 @@ 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); @@ -1525,16 +1606,32 @@ 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); @@ -1550,6 +1647,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; @@ -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); diff --git a/libsylph/socket.h b/libsylph/socket.h index 64d2d6a1..9c761305 100644 --- a/libsylph/socket.h +++ b/libsylph/socket.h @@ -86,6 +86,8 @@ gint sock_cleanup (void); gint sock_set_io_timeout (guint sec); +SockInfo *sock_new (const gchar *hostname, gushort port); + gint sock_set_nonblocking_mode (SockInfo *sock, gboolean nonblock); gboolean sock_is_nonblocking_mode (SockInfo *sock); @@ -109,6 +111,16 @@ gint sock_connect_async_thread (const gchar *hostname, gushort port); gint sock_connect_async_thread_wait (gint id, SockInfo **sock); #endif +gint sock_info_connect (SockInfo *sock); +#ifdef G_OS_UNIX +gint sock_info_connect_async (SockInfo *sock, + SockConnectFunc func, gpointer data); +#endif +#if USE_THREADS +gint sock_info_connect_async_thread (SockInfo *sock); +gint sock_info_connect_async_thread_wait(gint id, SockInfo **sock); +#endif + /* Basic I/O functions */ gint sock_printf (SockInfo *sock, const gchar *format, ...) G_GNUC_PRINTF(2, 3); -- cgit v1.2.3