From eb08bcfacfb330a8c120c7a94fe2df3155960d5b Mon Sep 17 00:00:00 2001 From: TingPing Date: Mon, 29 Dec 2014 01:42:49 -0500 Subject: [PATCH] Use GTask for async connecting Previously a child process was used on Unix and a thread on Windows and they each communicated with the main thread via pipes which includeded platform specific code and upon closure on Windows could result in crash. Now a thread is used on all platforms with safer ways of cancellation. Little behavior should have been changed though timing of print events are now after connecting finished which might be a minor issue. --- configure.ac | 6 +- src/common/cfgfiles.c | 2 - src/common/hexchat.h | 4 +- src/common/network.c | 11 +- src/common/server.c | 556 +++++++++++++++++---------------------- src/common/textevents.in | 4 +- src/common/util.c | 28 -- src/common/util.h | 3 - 8 files changed, 257 insertions(+), 357 deletions(-) diff --git a/configure.ac b/configure.ac index d93b2e04..9bc2a6b4 100644 --- a/configure.ac +++ b/configure.ac @@ -175,11 +175,11 @@ dnl ********************************************************************* dnl ** GLIB ************************************************************* dnl ********************************************************************* -AM_PATH_GLIB_2_0([2.32.0], [], [AC_MSG_ERROR([Glib not found!])], [gmodule gobject gio]) +AM_PATH_GLIB_2_0([2.36.0], [], [AC_MSG_ERROR([Glib not found!])], [gmodule gobject gio]) COMMON_CFLAGS="$GLIB_CFLAGS -DG_DISABLE_SINGLE_INCLUDES" COMMON_LIBS="$GLIB_LIBS" -AC_DEFINE([GLIB_VERSION_MIN_REQUIRED], [GLIB_VERSION_2_32], [Dont warn using older APIs]) -AC_DEFINE([GLIB_VERSION_MAX_ALLOWED], [GLIB_VERSION_2_32], [Prevents using newer APIs]) +AC_DEFINE([GLIB_VERSION_MIN_REQUIRED], [GLIB_VERSION_2_36], [Dont warn using older APIs]) +AC_DEFINE([GLIB_VERSION_MAX_ALLOWED], [GLIB_VERSION_2_36], [Prevents using newer APIs]) dnl ********************************************************************* dnl ** GTK ************************************************************** diff --git a/src/common/cfgfiles.c b/src/common/cfgfiles.c index 5de72dbd..52a2ca48 100644 --- a/src/common/cfgfiles.c +++ b/src/common/cfgfiles.c @@ -529,9 +529,7 @@ const struct prefs vars[] = {"irc_whois_front", P_OFFINT (hex_irc_whois_front), TYPE_BOOL}, {"net_auto_reconnect", P_OFFINT (hex_net_auto_reconnect), TYPE_BOOL}, -#ifndef WIN32 /* FIXME fix reconnect crashes and remove this ifdef! */ {"net_auto_reconnectonfail", P_OFFINT (hex_net_auto_reconnectonfail), TYPE_BOOL}, -#endif {"net_bind_host", P_OFFSET (hex_net_bind_host), TYPE_STR}, {"net_ping_timeout", P_OFFINT (hex_net_ping_timeout), TYPE_INT}, {"net_proxy_auth", P_OFFINT (hex_net_proxy_auth), TYPE_BOOL}, diff --git a/src/common/hexchat.h b/src/common/hexchat.h index a5b61a26..94bd3afe 100644 --- a/src/common/hexchat.h +++ b/src/common/hexchat.h @@ -487,9 +487,7 @@ typedef struct server #else void *ssl; #endif - int childread; - int childwrite; - int childpid; + GCancellable *cancellable; /* to cancel connecting thread */ int iotag; int recondelay_tag; /* reconnect delay timeout */ int joindelay_tag; /* waiting before we send JOIN */ diff --git a/src/common/network.c b/src/common/network.c index fcdaf547..3a5e9b14 100644 --- a/src/common/network.c +++ b/src/common/network.c @@ -120,10 +120,13 @@ net_resolve (netstore * ns, char *hostname, int port, char **real_host) getnameinfo (ns->ip6_hostent->ai_addr, ns->ip6_hostent->ai_addrlen, ipstring, sizeof (ipstring), NULL, 0, NI_NUMERICHOST); - if (ns->ip6_hostent->ai_canonname) - *real_host = g_strdup (ns->ip6_hostent->ai_canonname); - else - *real_host = g_strdup (hostname); + if (real_host) + { + if (ns->ip6_hostent->ai_canonname) + *real_host = g_strdup (ns->ip6_hostent->ai_canonname); + else + *real_host = g_strdup (hostname); + } return g_strdup (ipstring); } diff --git a/src/common/server.c b/src/common/server.c index 2b34dbde..d0698342 100644 --- a/src/common/server.c +++ b/src/common/server.c @@ -65,6 +65,8 @@ #include #endif +#define HEXCHAT_CONNECTION_ERROR g_quark_from_static_string("hexchat-connection-error") + #ifdef USE_OPENSSL /* local variables */ static struct session *g_sess = NULL; @@ -82,6 +84,14 @@ static void server_connect (server *serv, char *hostname, int port, int no_login extern pxProxyFactory *libproxy_factory; #endif +enum +{ + RESOLVE_FAILED = 1, + CONNECTION_FAILED, + BIND_FAILED, + PROXY_FAILED +}; + /* actually send to the socket. This might do a character translation or send via SSL. server/dcc both use this function. */ @@ -538,25 +548,6 @@ server_stopconnecting (server * serv) serv->joindelay_tag = 0; } -#ifndef WIN32 - /* kill the child process trying to connect */ - kill (serv->childpid, SIGKILL); - waitpid (serv->childpid, NULL, 0); - - close (serv->childwrite); - close (serv->childread); -#else - PostThreadMessage (serv->childpid, WM_QUIT, 0, 0); - - { - /* if we close the pipe now, giowin32 will crash. */ - int *pipefd = g_new (int, 2); - pipefd[0] = serv->childwrite; - pipefd[1] = serv->childread; - g_idle_add ((GSourceFunc)server_close_pipe, pipefd); - } -#endif - #ifdef USE_OPENSSL if (serv->ssl_do_connect_tag) { @@ -565,6 +556,12 @@ server_stopconnecting (server * serv) } #endif + if (serv->cancellable) + { + g_object_unref (serv->cancellable); + serv->cancellable = NULL; + } + fe_progressbar_end (serv); serv->connecting = FALSE; @@ -886,124 +883,6 @@ server_connect_success (server *serv) server_connected (serv); } -/* receive info from the child-process about connection progress */ - -static gboolean -server_read_child (GIOChannel *source, GIOCondition condition, server *serv) -{ - session *sess = serv->server_session; - char tbuf[128]; - char outbuf[512]; - char host[100]; - char ip[100]; - - waitline2 (source, tbuf, sizeof tbuf); - - switch (tbuf[0]) - { - case '0': /* print some text */ - waitline2 (source, tbuf, sizeof tbuf); - PrintText (serv->server_session, tbuf); - break; - case '1': /* unknown host */ - server_stopconnecting (serv); - closesocket (serv->sok4); - if (serv->proxy_sok4 != -1) - closesocket (serv->proxy_sok4); - if (serv->sok6 != -1) - closesocket (serv->sok6); - if (serv->proxy_sok6 != -1) - closesocket (serv->proxy_sok6); - EMIT_SIGNAL (XP_TE_UKNHOST, sess, NULL, NULL, NULL, NULL, 0); - if (!servlist_cycle (serv)) - if (prefs.hex_net_auto_reconnectonfail) - auto_reconnect (serv, FALSE, -1); - break; - case '2': /* connection failed */ - waitline2 (source, tbuf, sizeof tbuf); - server_stopconnecting (serv); - closesocket (serv->sok4); - if (serv->proxy_sok4 != -1) - closesocket (serv->proxy_sok4); - if (serv->sok6 != -1) - closesocket (serv->sok6); - if (serv->proxy_sok6 != -1) - closesocket (serv->proxy_sok6); - EMIT_SIGNAL (XP_TE_CONNFAIL, sess, errorstring (atoi (tbuf)), NULL, - NULL, NULL, 0); - if (!servlist_cycle (serv)) - if (prefs.hex_net_auto_reconnectonfail) - auto_reconnect (serv, FALSE, -1); - break; - case '3': /* gethostbyname finished */ - waitline2 (source, host, sizeof host); - waitline2 (source, ip, sizeof ip); - waitline2 (source, outbuf, sizeof outbuf); - EMIT_SIGNAL (XP_TE_CONNECT, sess, host, ip, outbuf, NULL, 0); - break; - case '4': /* success */ - waitline2 (source, tbuf, sizeof (tbuf)); - serv->sok = atoi (tbuf); - /* close the one we didn't end up using */ - if (serv->sok == serv->sok4) - closesocket (serv->sok6); - else - closesocket (serv->sok4); - if (serv->proxy_sok != -1) - { - if (serv->proxy_sok == serv->proxy_sok4) - closesocket (serv->proxy_sok6); - else - closesocket (serv->proxy_sok4); - } - - { - struct sockaddr addr; - int addr_len = sizeof (addr); - guint16 port; - - if (!getsockname (serv->sok, &addr, &addr_len)) - { - if (addr.sa_family == AF_INET) - port = ntohs(((struct sockaddr_in *)&addr)->sin_port); - else - port = ntohs(((struct sockaddr_in6 *)&addr)->sin6_port); - - g_snprintf (outbuf, sizeof (outbuf), "IDENTD %"G_GUINT16_FORMAT" ", port); - if (serv->network && ((ircnet *)serv->network)->user) - g_strlcat (outbuf, ((ircnet *)serv->network)->user, sizeof (outbuf)); - else - g_strlcat (outbuf, prefs.hex_irc_user_name, sizeof (outbuf)); - - handle_command (serv->server_session, outbuf, FALSE); - } - } - - server_connect_success (serv); - break; - case '5': /* prefs ip discovered */ - waitline2 (source, tbuf, sizeof tbuf); - prefs.local_ip = inet_addr (tbuf); - break; - case '7': /* gethostbyname (prefs.hex_net_bind_host) failed */ - sprintf (outbuf, - _("Cannot resolve hostname %s\nCheck your IP Settings!\n"), - prefs.hex_net_bind_host); - PrintText (sess, outbuf); - break; - case '8': - PrintText (sess, _("Proxy traversal failed.\n")); - server_disconnect (sess, FALSE, -1); - break; - case '9': - waitline2 (source, tbuf, sizeof tbuf); - EMIT_SIGNAL (XP_TE_SERVERLOOKUP, sess, tbuf, NULL, NULL, NULL, 0); - break; - } - - return TRUE; -} - /* kill all sockets & iotags of a server. Stop a connection attempt, or disconnect if already connected. */ @@ -1072,7 +951,6 @@ server_disconnect (session * sess, int sendquit, int err) { server *serv = sess->server; GSList *list; - char tbuf[64]; gboolean shutup = FALSE; /* send our QUIT reason */ @@ -1090,8 +968,8 @@ server_disconnect (session * sess, int sendquit, int err) notc_msg (sess); return; case 1: /* it was in the process of connecting */ - sprintf (tbuf, "%d", sess->server->childpid); - EMIT_SIGNAL (XP_TE_STOPCONNECT, sess, tbuf, NULL, NULL, NULL, 0); + EMIT_SIGNAL (XP_TE_STOPCONNECT, sess, NULL, NULL, NULL, NULL, 0); + g_cancellable_cancel (serv->cancellable); return; case 3: shutup = TRUE; /* won't print "disconnected" in channels */ @@ -1124,15 +1002,6 @@ server_disconnect (session * sess, int sendquit, int err) notify_cleanup (); } -/* send a "print text" command to the parent process - MUST END IN \n! */ - -static void -proxy_error (int fd, char *msg) -{ - write (fd, "0\n", 2); - write (fd, msg, strlen (msg)); -} - struct sock_connect { char version; @@ -1147,7 +1016,7 @@ struct sock_connect * 1 socks traversal failed */ static int -traverse_socks (int print_fd, int sok, char *serverAddr, int port) +traverse_socks (int sok, char *serverAddr, int port, GError **error) { struct sock_connect sc; unsigned char buf[256]; @@ -1165,7 +1034,7 @@ traverse_socks (int print_fd, int sok, char *serverAddr, int port) return 0; g_snprintf (buf, sizeof (buf), "SOCKS\tServer reported error %d,%d.\n", buf[0], buf[1]); - proxy_error (print_fd, buf); + *error = g_error_new_literal (HEXCHAT_CONNECTION_ERROR, PROXY_FAILED, buf); return 1; } @@ -1177,7 +1046,7 @@ struct sock5_connect1 }; static int -traverse_socks5 (int print_fd, int sok, char *serverAddr, int port) +traverse_socks5 (int sok, char *serverAddr, int port, GError **error) { struct sock5_connect1 sc1; unsigned char *sc2; @@ -1197,7 +1066,7 @@ traverse_socks5 (int print_fd, int sok, char *serverAddr, int port) if (buf[0] != 5) { - proxy_error (print_fd, "SOCKS\tServer is not socks version 5.\n"); + *error = g_error_new_literal (HEXCHAT_CONNECTION_ERROR, PROXY_FAILED, "SOCKS\tServer is not socks version 5.\n"); return 1; } @@ -1212,7 +1081,7 @@ traverse_socks5 (int print_fd, int sok, char *serverAddr, int port) /* authentication sub-negotiation (RFC1929) */ if (buf[1] != 2) /* UPA not supported by server */ { - proxy_error (print_fd, "SOCKS\tServer doesn't support UPA authentication.\n"); + *error = g_error_new_literal (HEXCHAT_CONNECTION_ERROR, PROXY_FAILED, "SOCKS\tServer doesn't support UPA authentication.\n"); return 1; } @@ -1232,7 +1101,7 @@ traverse_socks5 (int print_fd, int sok, char *serverAddr, int port) goto read_error; if ( buf[1] != 0 ) { - proxy_error (print_fd, "SOCKS\tAuthentication failed. " + *error = g_error_new_literal (HEXCHAT_CONNECTION_ERROR, PROXY_FAILED, "SOCKS\tAuthentication failed. " "Is username and password correct?\n"); return 1; /* UPA failed! */ } @@ -1241,7 +1110,7 @@ traverse_socks5 (int print_fd, int sok, char *serverAddr, int port) { if (buf[1] != 0) { - proxy_error (print_fd, "SOCKS\tAuthentication required but disabled in settings.\n"); + *error = g_error_new_literal (HEXCHAT_CONNECTION_ERROR, PROXY_FAILED, "SOCKS\tAuthentication required but disabled in settings.\n"); return 1; } } @@ -1268,7 +1137,7 @@ traverse_socks5 (int print_fd, int sok, char *serverAddr, int port) g_snprintf (buf, sizeof (buf), "SOCKS\tProxy refused to connect to host (not allowed).\n"); else g_snprintf (buf, sizeof (buf), "SOCKS\tProxy failed to connect to host (error %d).\n", buf[1]); - proxy_error (print_fd, buf); + *error = g_error_new_literal (HEXCHAT_CONNECTION_ERROR, PROXY_FAILED, buf); return 1; } if (buf[3] == 1) /* IPV4 32bit address */ @@ -1291,12 +1160,12 @@ traverse_socks5 (int print_fd, int sok, char *serverAddr, int port) return 0; /* success */ read_error: - proxy_error (print_fd, "SOCKS\tRead error from server.\n"); + *error = g_error_new_literal (HEXCHAT_CONNECTION_ERROR, PROXY_FAILED, "SOCKS\tRead error from server.\n"); return 1; } static int -traverse_wingate (int print_fd, int sok, char *serverAddr, int port) +traverse_wingate (int sok, char *serverAddr, int port, GError **error) { char buf[128]; @@ -1358,30 +1227,7 @@ base64_encode (char *to, char *from, unsigned int len) } static int -http_read_line (int print_fd, int sok, char *buf, int len) -{ - len = waitline (sok, buf, len, TRUE); - if (len >= 1) - { - /* print the message out (send it to the parent process) */ - write (print_fd, "0\n", 2); - - if (buf[len-1] == '\r') - { - buf[len-1] = '\n'; - write (print_fd, buf, len); - } else - { - write (print_fd, buf, len); - write (print_fd, "\n", 1); - } - } - - return len; -} - -static int -traverse_http (int print_fd, int sok, char *serverAddr, int port) +traverse_http (int sok, char *serverAddr, int port, GError **error) { char buf[512]; char auth_data[256]; @@ -1400,16 +1246,18 @@ traverse_http (int print_fd, int sok, char *serverAddr, int port) n += g_snprintf (buf+n, sizeof (buf)-n, "\r\n"); send (sok, buf, n, 0); - n = http_read_line (print_fd, sok, buf, sizeof (buf)); + n = waitline (sok, buf, sizeof(buf), TRUE); + /* "HTTP/1.0 200 OK" */ - if (n < 12) - return 1; - if (memcmp (buf, "HTTP/", 5) || memcmp (buf + 9, "200", 3)) + if (n < 12 || (memcmp (buf, "HTTP/", 5) || memcmp (buf + 9, "200", 3))) + { + *error = g_error_new_literal (HEXCHAT_CONNECTION_ERROR, PROXY_FAILED, buf); return 1; + } while (1) { /* read until blank line */ - n = http_read_line (print_fd, sok, buf, sizeof (buf)); + n = waitline (sok, buf, sizeof(buf), TRUE); if (n < 1 || (n == 1 && buf[0] == '\n')) break; } @@ -1417,63 +1265,203 @@ traverse_http (int print_fd, int sok, char *serverAddr, int port) } static int -traverse_proxy (int proxy_type, int print_fd, int sok, char *ip, int port, netstore *ns_proxy, int csok4, int csok6, int *csok, char bound) +traverse_proxy (int proxy_type, int sok, char *ip, int port, GError **error) { switch (proxy_type) { case 1: - return traverse_wingate (print_fd, sok, ip, port); + return traverse_wingate (sok, ip, port, error); case 2: - return traverse_socks (print_fd, sok, ip, port); + return traverse_socks (sok, ip, port, error); case 3: - return traverse_socks5 (print_fd, sok, ip, port); + return traverse_socks5 (sok, ip, port, error); case 4: - return traverse_http (print_fd, sok, ip, port); + return traverse_http (sok, ip, port, error); } return 1; } -/* this is the child process making the connection attempt */ +struct connect_data +{ + server *serv; + char *local_ip; + char *hostname; + char *proxy_hostname; + char *ip; + char *port; +}; -static int -server_child (server * serv) +static void +connect_data_free (struct connect_data *data) +{ + if (data) + { + g_free (data->local_ip); + g_free (data->hostname); + g_free (data->proxy_hostname); + g_free (data->ip); + g_free (data->port); + g_free (data); + } +} + +static void +server_connect_finish (GObject *source, GAsyncResult *res, gpointer user_data) +{ + GError *error = NULL; + server *serv; + session *sess; + struct connect_data *data; + gboolean retry = FALSE; + char buf[256]; + + data = (struct connect_data*)g_task_get_task_data (G_TASK(res)); + serv = data->serv; + + if (!is_server(serv)) + { + g_warning ("Server destroyed before connection finished!\n"); + return; + } + + sess = serv->server_session; + + /* These print after the fact, but should be good enough */ + if (data->proxy_hostname) + EMIT_SIGNAL (XP_TE_SERVERLOOKUP, sess, data->proxy_hostname, NULL, NULL, NULL, 0); + + EMIT_SIGNAL (XP_TE_CONNECT, sess, data->hostname, data->ip, data->port, NULL, 0); + + /* Update local ip for dcc */ + if (data->local_ip) + prefs.local_ip = inet_addr (data->local_ip); + + serv->sok = g_task_propagate_int (G_TASK(res), &error); + if (!error) + { + if (serv->sok == serv->sok4) + closesocket (serv->sok6); + else + closesocket (serv->sok4); + if (serv->proxy_sok != -1) + { + if (serv->proxy_sok == serv->proxy_sok4) + closesocket (serv->proxy_sok6); + else + closesocket (serv->proxy_sok4); + } + { + struct sockaddr addr; + int addr_len = sizeof (addr); + guint16 port; + + if (!getsockname (serv->sok, &addr, &addr_len)) + { + if (addr.sa_family == AF_INET) + port = ntohs(((struct sockaddr_in *)&addr)->sin_port); + else + port = ntohs(((struct sockaddr_in6 *)&addr)->sin6_port); + + g_snprintf (buf, sizeof (buf), "IDENTD %"G_GUINT16_FORMAT" ", port); + if (serv->network && ((ircnet *)serv->network)->user) + g_strlcat (buf, ((ircnet *)serv->network)->user, sizeof (buf)); + else + g_strlcat (buf, prefs.hex_irc_user_name, sizeof (buf)); + + handle_command (serv->server_session, buf, FALSE); + } + } + server_connect_success (serv); + return; + } + + g_warning ("%s\n", error->message); + + switch (error->code) + { + case RESOLVE_FAILED: + retry = TRUE; + EMIT_SIGNAL (XP_TE_UKNHOST, sess, NULL, NULL, NULL, NULL, 0); + goto close_connection; + case CONNECTION_FAILED: + retry = TRUE; + EMIT_SIGNAL (XP_TE_CONNFAIL, sess, error->message, NULL, NULL, NULL, 0); + goto close_connection; + case BIND_FAILED: + PrintText (sess, error->message); + break; + case PROXY_FAILED: + PrintTextf (sess, _("Proxy Traveral Failed: %s\n"), error->message); + server_disconnect (sess, FALSE, -1); + break; + case 9: + EMIT_SIGNAL (XP_TE_SERVERLOOKUP, sess, error->message, NULL, NULL, NULL, 0); + break; + case G_IO_ERROR_CANCELLED: + EMIT_SIGNAL (XP_TE_STOPCONNECT, sess, NULL, NULL, NULL, NULL, 0); + goto close_connection; + default: + g_warning ("%d\n", error->code); + g_assert_not_reached (); + } + + return; + +close_connection: + server_stopconnecting (serv); + closesocket (serv->sok4); + if (serv->proxy_sok4 != -1) + closesocket (serv->proxy_sok4); + if (serv->sok6 != -1) + closesocket (serv->sok6); + if (serv->proxy_sok6 != -1) + closesocket (serv->proxy_sok6); + + if (retry && !servlist_cycle (serv) && prefs.hex_net_auto_reconnectonfail) + auto_reconnect (serv, FALSE, -1); +} + +static void +server_connect_thread (GTask *task, gpointer source, gpointer task_data, GCancellable *cancellable) { netstore *ns_server; - netstore *ns_proxy = NULL; - netstore *ns_local; + struct connect_data *data = (struct connect_data*)task_data; + server *serv = data->serv; int port = serv->port; int error; - int sok, psok; + int sok = -1, psok; char *hostname = serv->hostname; - char *real_hostname = NULL; - char *ip; char *proxy_ip = NULL; - char *local_ip; int connect_port; - char buf[512]; - char bound = 0; int proxy_type = 0; char *proxy_host = NULL; int proxy_port; + if (!is_server(serv)) + { + g_warning ("Server destroyed before connection started!\n"); + return; + } + ns_server = net_store_new (); - /* is a hostname set? - bind to it */ if (prefs.hex_net_bind_host[0]) { - ns_local = net_store_new (); - local_ip = net_resolve (ns_local, prefs.hex_net_bind_host, 0, &real_hostname); - if (local_ip != NULL) + netstore *ns_local = net_store_new (); + + data->local_ip = net_resolve (ns_local, prefs.hex_net_bind_host, 0, NULL); + if (data->local_ip != NULL) { - g_snprintf (buf, sizeof (buf), "5\n%s\n", local_ip); - write (serv->childwrite, buf, strlen (buf)); net_bind (ns_local, serv->sok4, serv->sok6); - bound = 1; } else { - write (serv->childwrite, "7\n", 2); + g_task_return_new_error (task, HEXCHAT_CONNECTION_ERROR, BIND_FAILED, + "Cannot resolve hostname %s\nCheck your IP Settings!\n", prefs.hex_net_bind_host); + net_store_destroy (ns_local); + goto error_cleanup; } + net_store_destroy (ns_local); } @@ -1523,49 +1511,49 @@ server_child (server * serv) proxy_port = prefs.hex_net_proxy_port; } } - serv->proxy_type = proxy_type; + data->proxy_hostname = proxy_host; - /* first resolve where we want to connect to */ if (proxy_type > 0) { - g_snprintf (buf, sizeof (buf), "9\n%s\n", proxy_host); - write (serv->childwrite, buf, strlen (buf)); - ip = net_resolve (ns_server, proxy_host, proxy_port, &real_hostname); - g_free (proxy_host); - if (!ip) + data->ip = net_resolve (ns_server, proxy_host, proxy_port, &data->hostname); + if (!data->ip) { - write (serv->childwrite, "1\n", 2); - goto xit; + g_task_return_new_error (task, HEXCHAT_CONNECTION_ERROR, RESOLVE_FAILED, + "Unknown hostname %s\n", proxy_host); + goto error_cleanup; } connect_port = proxy_port; - /* if using socks4 or MS Proxy, attempt to resolve ip for irc server */ + /* if using socks4, attempt to resolve ip for irc server */ if ((proxy_type == 2) || (proxy_type == 5)) { - ns_proxy = net_store_new (); - proxy_ip = net_resolve (ns_proxy, hostname, port, &real_hostname); + netstore *ns_proxy = net_store_new (); + g_free (data->hostname); + proxy_ip = net_resolve (ns_proxy, hostname, port, &data->hostname); + net_store_destroy (ns_proxy); if (!proxy_ip) { - write (serv->childwrite, "1\n", 2); - goto xit; + g_task_return_new_error (task, HEXCHAT_CONNECTION_ERROR, RESOLVE_FAILED, + "Unknown hostname %s\n", hostname); + goto error_cleanup; } } else /* otherwise we can just use the hostname */ proxy_ip = g_strdup (hostname); - } else + } + else { - ip = net_resolve (ns_server, hostname, port, &real_hostname); - if (!ip) + data->ip = net_resolve (ns_server, hostname, port, &data->hostname); + if (!data->ip) { - write (serv->childwrite, "1\n", 2); - goto xit; + g_task_return_new_error (task, HEXCHAT_CONNECTION_ERROR, RESOLVE_FAILED, + "Unknown hostname %s\n", hostname); + goto error_cleanup; } connect_port = port; } - g_snprintf (buf, sizeof (buf), "3\n%s\n%s\n%d\n", - real_hostname, ip, connect_port); - write (serv->childwrite, buf, strlen (buf)); + data->port = g_strdup_printf ("%d", connect_port); if (!serv->dont_use_proxy && (proxy_type == 5)) error = net_connect (ns_server, serv->proxy_sok4, serv->proxy_sok6, &psok); @@ -1577,54 +1565,33 @@ server_child (server * serv) if (error != 0) { - g_snprintf (buf, sizeof (buf), "2\n%d\n", sock_error ()); - write (serv->childwrite, buf, strlen (buf)); - } else + g_task_return_new_error (task, HEXCHAT_CONNECTION_ERROR, CONNECTION_FAILED, "%s", errorstring (sock_error())); + goto error_cleanup; + } + else { + GError *err = NULL; /* connect succeeded */ - if (proxy_ip) + if (proxy_ip && traverse_proxy (proxy_type, psok, proxy_ip, port, &err)) { - switch (traverse_proxy (proxy_type, serv->childwrite, psok, proxy_ip, port, ns_proxy, serv->sok4, serv->sok6, &sok, bound)) - { - case 0: /* success */ - g_snprintf (buf, sizeof (buf), "4\n%d\n", sok); /* success */ - write (serv->childwrite, buf, strlen (buf)); - break; - case 1: /* socks traversal failed */ - write (serv->childwrite, "8\n", 2); - break; - } - } else - { - g_snprintf (buf, sizeof (buf), "4\n%d\n", sok); /* success */ - write (serv->childwrite, buf, strlen (buf)); + g_task_return_error (task, err); + goto error_cleanup; } } -xit: - - /* this is probably not needed */ +error_cleanup: net_store_destroy (ns_server); - if (ns_proxy) - net_store_destroy (ns_proxy); - - /* no need to free ip/real_hostname, this process is exiting */ -#ifdef WIN32 - /* under win32 we use a thread -> shared memory, must free! */ g_free (proxy_ip); - g_free (ip); - g_free (real_hostname); -#endif - - return 0; - /* cppcheck-suppress memleak */ + g_task_return_int (task, sok); /* Does nothing if error'd */ + return; } static void server_connect (server *serv, char *hostname, int port, int no_login) { - int pid, read_des[2]; session *sess = serv->server_session; + GTask *task; + struct connect_data *data; #ifdef USE_OPENSSL if (!serv->ctx && serv->use_ssl) @@ -1700,55 +1667,20 @@ server_connect (server *serv, char *hostname, int port, int no_login) fe_set_away (serv); server_flush_queue (serv); -#ifdef WIN32 - if (_pipe (read_des, 4096, _O_BINARY) < 0) -#else - if (pipe (read_des) < 0) -#endif - return; -#ifdef __EMX__ /* os/2 */ - setmode (read_des[0], O_BINARY); - setmode (read_des[1], O_BINARY); -#endif - serv->childread = read_des[0]; - serv->childwrite = read_des[1]; - /* create both sockets now, drop one later */ net_sockets (&serv->sok4, &serv->sok6); serv->proxy_sok4 = -1; serv->proxy_sok6 = -1; -#ifdef WIN32 - CloseHandle (CreateThread (NULL, 0, - (LPTHREAD_START_ROUTINE)server_child, - serv, 0, (DWORD *)&pid)); -#else -#ifdef LOOKUPD - /* CL: net_resolve calls rand() when LOOKUPD is set, so prepare a different - * seed for each child. This method gives a bigger variation in seed values - * than calling srand(time(0)) in the child itself. - */ - rand(); -#endif - switch (pid = fork ()) - { - case -1: - return; - - case 0: - /* this is the child */ - setuid (getuid ()); - server_child (serv); - _exit (0); - } -#endif - serv->childpid = pid; -#ifdef WIN32 - serv->iotag = fe_input_add (serv->childread, FIA_READ|FIA_FD, server_read_child, -#else - serv->iotag = fe_input_add (serv->childread, FIA_READ, server_read_child, -#endif - serv); + /* start the connection in a thread */ + data = g_new0 (struct connect_data, 1); + data->serv = serv; + serv->cancellable = g_cancellable_new (); + task = g_task_new (NULL, serv->cancellable, server_connect_finish, NULL); + g_task_set_task_data (task, data, (GDestroyNotify)connect_data_free); + g_task_set_return_on_cancel (task, TRUE); + g_task_run_in_thread (task, server_connect_thread); + g_object_unref (task); } void diff --git a/src/common/textevents.in b/src/common/textevents.in index 26e2b3e3..aa01182c 100644 --- a/src/common/textevents.in +++ b/src/common/textevents.in @@ -751,8 +751,8 @@ n2 Stop Connection XP_TE_STOPCONNECT pevt_sconnect_help -%C23*%O$tStopped previous connection attempt (%C24$1%O) -1 +%C23*%O$tStopped previous connection attempt +0 Topic XP_TE_TOPIC diff --git a/src/common/util.c b/src/common/util.c index 37d214c0..e47a44dd 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -207,34 +207,6 @@ waitline (int sok, char *buf, int bufsize, int use_recv) } } -#ifdef WIN32 -/* waitline2 using win32 file descriptor and glib instead of _read. win32 can't _read() sok! */ -int -waitline2 (GIOChannel *source, char *buf, int bufsize) -{ - int i = 0; - gsize len; - GError *error = NULL; - - while (1) - { - g_io_channel_set_buffered (source, FALSE); - g_io_channel_set_encoding (source, NULL, &error); - - if (g_io_channel_read_chars (source, &buf[i], 1, &len, &error) != G_IO_STATUS_NORMAL) - { - return -1; - } - if (buf[i] == '\n' || bufsize == i + 1) - { - buf[i] = 0; - return i; - } - i++; - } -} -#endif - /* checks for "~" in a file and expands */ char * diff --git a/src/common/util.h b/src/common/util.h index 8b2762fb..1b68a462 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -61,10 +61,7 @@ int strip_hidden_attribute (char *src, char *dst); char *errorstring (int err); int waitline (int sok, char *buf, int bufsize, int); #ifdef WIN32 -int waitline2 (GIOChannel *source, char *buf, int bufsize); int get_cpu_arch (void); -#else -#define waitline2(source,buf,size) waitline(serv->childread,buf,size,0) #endif unsigned long make_ping_time (void); void move_file (char *src_dir, char *dst_dir, char *fname, int dccpermissions);