changeset 4366:7ba9b56a8796

[gaim-migrate @ 4632] Ok, so this is a patch by Nicolas Lichtmaier (niqueco). You've probably noticed that when you try to sign an account on, the UI freezes for a while (esp. on poor connections). This was because gethostbyname() blocks. Nicolas's patch here forks a new process to resolve host names and returns them in a pipe. It makes things smoother and faster. Thanks so much, Nicolas. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Mon, 20 Jan 2003 19:26:56 +0000
parents 6e96ced6fb78
children 101fa831f52f
files ChangeLog src/protocols/jabber/jabber.c src/protocols/oscar/oscar.c src/proxy.c src/proxy.h
diffstat 5 files changed, 581 insertions(+), 137 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Jan 20 18:12:14 2003 +0000
+++ b/ChangeLog	Mon Jan 20 19:26:56 2003 +0000
@@ -16,7 +16,9 @@
 	  Robert McQueen)
 	* Conversation backend and UI are now separated. (Thanks,
 	  Christian Hammond)
-
+	* Asynchronous, non-blocking, DNS function (Thanks, Nicolas
+	  Lichtmaier)
+	
 	Plugins:
 	* Tray icon plugin--replaces the old GNOME applet. You'll need 
 	  the panel Notification Area applet (aka system-tray-applet) 
--- a/src/protocols/jabber/jabber.c	Mon Jan 20 18:12:14 2003 +0000
+++ b/src/protocols/jabber/jabber.c	Mon Jan 20 19:26:56 2003 +0000
@@ -765,8 +765,7 @@
 	jd = gc->proto_data;
 	gjc = jd->gjc;
 
-	if (gjc->fd != source)
-		gjc->fd = source;
+	gjc->fd = source;
 
 	if (source == -1) {
 		STATE_EVT(JCONN_STATE_OFF)
@@ -798,7 +797,7 @@
 static void gjab_start(gjconn gjc)
 {
 	struct aim_user *user;
-	int port;
+	int port, rc;
 
 	if (!gjc || gjc->state != JCONN_STATE_OFF)
 		return;
@@ -811,8 +810,8 @@
 	XML_SetElementHandler(gjc->parser, startElement, endElement);
 	XML_SetCharacterDataHandler(gjc->parser, charData);
 
-	gjc->fd = proxy_connect(gjc->user->server, port, gjab_connected, GJ_GC(gjc));
-	if (!user->gc || (gjc->fd < 0)) {
+	rc = proxy_connect(gjc->user->server, port, gjab_connected, GJ_GC(gjc));
+	if (!user->gc || (rc < 0)) {
 		STATE_EVT(JCONN_STATE_OFF)
 		return;
 	}
--- a/src/protocols/oscar/oscar.c	Mon Jan 20 18:12:14 2003 +0000
+++ b/src/protocols/oscar/oscar.c	Mon Jan 20 19:26:56 2003 +0000
@@ -620,6 +620,8 @@
 	odata = gc->proto_data;
 	sess = odata->sess;
 	conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
+	
+	conn->fd = source;
 
 	if (source < 0) {
 		hide_login_progress(gc, _("Couldn't connect to host"));
@@ -639,6 +641,7 @@
 	char buf[256];
 	struct gaim_connection *gc = new_gaim_conn(user);
 	struct oscar_data *odata = gc->proto_data = g_new0(struct oscar_data, 1);
+	int rc;
 
 	if (isdigit(*user->username)) {
 		odata->icq = TRUE;
@@ -675,12 +678,12 @@
 	aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0);
 
 	conn->status |= AIM_CONN_STATUS_INPROGRESS;
-	conn->fd = proxy_connect(user->proto_opt[USEROPT_AUTH][0] ?
+	rc = proxy_connect(user->proto_opt[USEROPT_AUTH][0] ?
 					user->proto_opt[USEROPT_AUTH] : FAIM_LOGIN_SERVER,
 				 user->proto_opt[USEROPT_AUTHPORT][0] ?
 					atoi(user->proto_opt[USEROPT_AUTHPORT]) : FAIM_LOGIN_PORT,
 				 oscar_login_connect, gc);
-	if (conn->fd < 0) {
+	if (rc < 0) {
 		hide_login_progress(gc, _("Couldn't connect to host"));
 		signoff(gc);
 		return;
@@ -766,7 +769,8 @@
 
 	odata = gc->proto_data;
 	sess = odata->sess;
-	bosconn = odata->conn;
+	bosconn = odata->conn;	
+	bosconn->fd = source;
 
 	if (source < 0) {
 		hide_login_progress(gc, _("Could Not Connect"));
@@ -797,7 +801,7 @@
 static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	struct aim_authresp_info *info;
-	int i; char *host; int port;
+	int i, rc; char *host; int port;
 	struct aim_user *user;
 	aim_conn_t *bosconn;
 
@@ -921,9 +925,9 @@
 	}
 	host = g_strndup(info->bosip, i);
 	bosconn->status |= AIM_CONN_STATUS_INPROGRESS;
-	bosconn->fd = proxy_connect(host, port, oscar_bos_connect, gc);
+	rc = proxy_connect(host, port, oscar_bos_connect, gc);
 	g_free(host);
-	if (bosconn->fd < 0) {
+	if (rc < 0) {
 		hide_login_progress(gc, _("Could Not Connect"));
 		od->killme = TRUE;
 		return 0;
@@ -988,6 +992,8 @@
 	struct pieceofcrap *pos = data;
 	char buf[BUF_LONG];
 
+	pos->fd = source;
+
 	if (source < 0) {
 		char buf[256];
 		g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly.  You may want to use TOC until "
@@ -1079,7 +1085,6 @@
 		do_error_dialog(_("Gaim was Unable to get valid login hash."),
 				 buf, GAIM_WARNING);
 	}
-	pos->fd = fd;
 
 	return 1;
 }
@@ -1166,6 +1171,7 @@
 	odata = gc->proto_data;
 	sess = odata->sess;
 	tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_CHATNAV);
+	tstconn->fd = source;
 
 	if (source < 0) {
 		aim_conn_kill(sess, &tstconn);
@@ -1194,6 +1200,7 @@
 	odata = gc->proto_data;
 	sess = odata->sess;
 	tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
+	tstconn->fd = source;
 
 	if (source < 0) {
 		aim_conn_kill(sess, &tstconn);
@@ -1226,6 +1233,7 @@
 	odata = gc->proto_data;
 	sess = odata->sess;
 	tstconn = ccon->conn;
+	tstconn->fd = source;
 
 	if (source < 0) {
 		aim_conn_kill(sess, &tstconn);
@@ -1256,6 +1264,7 @@
 	odata = gc->proto_data;
 	sess = odata->sess;
 	tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_EMAIL);
+	tstconn->fd = source;
 
 	if (source < 0) {
 		aim_conn_kill(sess, &tstconn);
@@ -1275,7 +1284,7 @@
 	struct gaim_connection *gc = sess->aux_data;
 	struct aim_user *user = gc->user;
 	aim_conn_t *tstconn;
-	int i;
+	int i, rc;
 	char *host;
 	int port;
 
@@ -1309,8 +1318,8 @@
 		aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, gaim_account_confirm, 0);
 
 		tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
-		tstconn->fd = proxy_connect(host, port, oscar_auth_connect, gc);
-		if (tstconn->fd < 0) {
+		rc = proxy_connect(host, port, oscar_auth_connect, gc);
+		if (rc < 0) {
 			aim_conn_kill(sess, &tstconn);
 			debug_printf("unable to reconnect with authorizer\n");
 			g_free(host);
@@ -1329,8 +1338,8 @@
 		aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chatnav, 0);
 
 		tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
-		tstconn->fd = proxy_connect(host, port, oscar_chatnav_connect, gc);
-		if (tstconn->fd < 0) {
+		rc = proxy_connect(host, port, oscar_chatnav_connect, gc);
+		if (rc < 0) {
 			aim_conn_kill(sess, &tstconn);
 			debug_printf("unable to connect to chatnav server\n");
 			g_free(host);
@@ -1361,8 +1370,8 @@
 		ccon->show = extract_name(redir->chat.room);
 		
 		ccon->conn->status |= AIM_CONN_STATUS_INPROGRESS;
-		ccon->conn->fd = proxy_connect(host, port, oscar_chat_connect, ccon);
-		if (ccon->conn->fd < 0) {
+		rc = proxy_connect(host, port, oscar_chat_connect, ccon);
+		if (rc < 0) {
 			aim_conn_kill(sess, &tstconn);
 			debug_printf("unable to connect to chat server\n");
 			g_free(host);
@@ -1384,8 +1393,8 @@
 		aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_email, 0);
 
 		tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
-		tstconn->fd = proxy_connect(host, port, oscar_email_connect, gc);
-		if (tstconn->fd < 0) {
+		rc = proxy_connect(host, port, oscar_email_connect, gc);
+		if (rc < 0) {
 			aim_conn_kill(sess, &tstconn);
 			debug_printf("unable to connect to email server\n");
 			g_free(host);
@@ -1503,8 +1512,7 @@
 		return;
 	}
 
-	if (dim->conn->fd != source)
-		dim->conn->fd = source;
+	dim->conn->fd = source;
 	aim_conn_completeconnect(od->sess, dim->conn);
 	if (!(cnv = gaim_find_conversation(dim->name))) 
 		cnv = gaim_conversation_new(GAIM_CONV_IM, dim->name);
@@ -1720,7 +1728,7 @@
 	struct oscar_data *od;
 	struct direct_im *dim;
 	char *host; int port = 4443;
-	int i;
+	int i, rc;
 
 	if (!g_slist_find(connections, gc)) {
 		cancel_direct_im(d);
@@ -1760,9 +1768,9 @@
 	}
 	host = g_strndup(d->ip, i);
 	dim->conn->status |= AIM_CONN_STATUS_INPROGRESS;
-	dim->conn->fd = proxy_connect(host, port, oscar_directim_callback, dim);
+	rc = proxy_connect(host, port, oscar_directim_callback, dim);
 	g_free(host);
-	if (dim->conn->fd < 0) {
+	if (rc < 0) {
 		aim_conn_kill(od->sess, &dim->conn);
 		g_free(dim);
 		cancel_direct_im(d);
--- a/src/proxy.c	Mon Jan 20 18:12:14 2003 +0000
+++ b/src/proxy.c	Mon Jan 20 19:26:56 2003 +0000
@@ -37,6 +37,7 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <unistd.h>
+#include <signal.h>
 #else
 #include <winsock.h>
 #endif
@@ -55,7 +56,7 @@
 
 char proxyhost[128] = { 0 };
 int proxyport = 0;
-int proxytype = 0;
+proxytype_t proxytype = PROXY_NONE;
 char proxyuser[128] = { 0 };
 char proxypass[128] = { 0 };
 
@@ -65,6 +66,7 @@
 	char *host;
 	int port;
 	gint inpa;
+	proxytype_t proxytype;
 };
 
 typedef struct _GaimIOClosure {
@@ -129,16 +131,416 @@
 		g_source_remove(tag);
 }
 
-static struct sockaddr_in *gaim_gethostbyname(char *host, int port)
+
+typedef void (*dns_callback_t)(struct sockaddr *addr, size_t addrlen,
+	gpointer data, const char *error_message);
+
+#ifdef __unix__
+
+/*  This structure represents both a pending DNS request and
+ *  a free child process.
+ */
+typedef struct {
+	char *host;
+	int port;
+	int socktype;
+	dns_callback_t callback;
+	gpointer data;
+	gint inpa;
+	int fd_in, fd_out;
+	pid_t dns_pid;
+} pending_dns_request_t;
+
+static GSList *free_dns_children = NULL;
+static GQueue *queued_requests = NULL;
+
+static int number_of_dns_children = 0;
+
+const int MAX_DNS_CHILDREN = 2;
+
+typedef struct {
+	char hostname[512];
+	int port;
+	int socktype;
+} dns_params_t;
+
+typedef struct {
+	dns_params_t params;
+	dns_callback_t callback;
+	gpointer data;
+} queued_dns_request_t;
+
+static int send_dns_request_to_child(pending_dns_request_t *req, dns_params_t *dns_params)
+{
+	char ch;
+	int rc;
+
+	/* Are you alive? */
+	if(kill(req->dns_pid, 0) != 0) {
+		debug_printf("DNS child %d no longer exists\n", req->dns_pid);
+		return -1;
+	}
+	
+	/* Let's contact this lost child! */
+	rc = write(req->fd_in, dns_params, sizeof(*dns_params));
+	if(rc<0) {
+		debug_printf("Error writing to DNS child %d: %s\n", req->dns_pid, strerror(errno));
+		return -1;
+	}
+	
+	g_return_val_if_fail(rc == sizeof(*dns_params), -1);
+	
+	/* Did you hear me? (This avoids some race conditions) */
+	rc = read(req->fd_out, &ch, 1);
+	if(rc != 1 || ch!='Y') {
+		debug_printf("DNS child %d not responding: killing it!\n", req->dns_pid);
+		kill(req->dns_pid, SIGKILL);
+		return -1;
+	}
+	
+	debug_printf("Successfuly sent DNS request to child %d\n", req->dns_pid);
+	return 0;
+}
+
+static void host_resolved(gpointer data, gint source, GaimInputCondition cond);
+
+static void release_dns_child(pending_dns_request_t *req)
 {
-	static struct sockaddr_in sin;
+	g_free(req->host);
+
+	if(queued_requests && !g_queue_is_empty(queued_requests)) {
+		queued_dns_request_t *r = g_queue_pop_head(queued_requests);
+		req->host = g_strdup(r->params.hostname);
+		req->port = r->params.port;
+		req->socktype = r->params.socktype;
+		req->callback = r->callback;
+		req->data = r->data;
+
+		debug_printf("Processing queued DNS query for '%s' with child %d\n", req->host, req->dns_pid);
+
+		if(send_dns_request_to_child(req, &(r->params)) != 0) {
+			g_free(req->host);
+			g_free(req);
+			req = NULL;
+			number_of_dns_children--;
+			
+			debug_printf("Intent of process queued query of '%s' failed, requeing...\n",
+				r->params.hostname);
+			g_queue_push_head(queued_requests, r);
+		} else {
+			req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req);
+			g_free(r);
+		}
+		
+	} else {
+		req->host = NULL;
+		req->callback = NULL;
+		req->data = NULL;
+		free_dns_children = g_slist_append(free_dns_children, req);
+	}
+}
+
+static void host_resolved(gpointer data, gint source, GaimInputCondition cond)
+{
+	pending_dns_request_t *req = (pending_dns_request_t*)data;
+	int rc, err;
+	struct sockaddr *addr = NULL;
+	socklen_t addrlen;
+
+	debug_printf("Host '%s' resolved\n", req->host);
+	gaim_input_remove(req->inpa);
+
+	rc=read(req->fd_out, &err, sizeof(err));
+	if((rc==4) && (err!=0)) {
+		char message[1024];
+		g_snprintf(message, sizeof(message), "DNS error: %s (pid=%d)",
+#ifdef HAVE_GETADDRINFO
+			gai_strerror(err),
+#else
+			hstrerror(err),
+#endif
+			req->dns_pid);
+		debug_printf("%s\n",message);
+		req->callback(NULL, 0, req->data, message);
+		release_dns_child(req);	
+		return;
+	}
+	if(G_LIKELY(rc>0)) {
+		rc=read(req->fd_out, &addrlen, sizeof(addrlen));
+		if(G_LIKELY(rc>0)) {
+			addr=g_malloc(addrlen);
+			rc=read(req->fd_out, addr, addrlen);
+		}
+	}
+	if(G_UNLIKELY(rc==-1)) {
+		char message[1024];
+		g_snprintf(message, sizeof(message), "Error reading from DNS child: %s",strerror(errno));
+		debug_printf("%s\n",message);
+		close(req->fd_out);
+		req->callback(NULL, 0, req->data, message);
+		g_free(req->host);
+		g_free(req);
+		number_of_dns_children--;
+		return;
+	}
+	if(G_UNLIKELY(rc==0)) {
+		char message[1024];
+		g_snprintf(message, sizeof(message), "EOF reading from DNS child");
+		close(req->fd_out);
+		debug_printf("%s\n",message);
+		req->callback(NULL, 0, req->data, message);
+		g_free(req->host);
+		g_free(req);
+		number_of_dns_children--;
+		return;
+	}
+
+/*	wait4(req->dns_pid, NULL, WNOHANG, NULL); */
+
+	req->callback(addr, addrlen, req->data, NULL);
+	
+	g_free(addr);
+
+	release_dns_child(req);	
+}
+
+static void trap_gdb_bug()
+{
+	const char *message =
+		"Gaim's DNS child got a SIGTRAP signal. \n"
+		"This can be caused by trying to run gaim into gdb.\n"
+		"There's a known gdb bug which prevent this, supposedly gaim\n"
+		"should have detected you were using gdb and use an ugly hack,\n"
+		"check cope_with_gdb_brokenness() in proxy.c.\n\n"
+		"For more info about the bug check http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n";
+	fputs("\n* * *\n",stderr);
+	fputs(message,stderr);
+	fputs("* * *\n\n",stderr);
+	execlp("xmessage","xmessage","-center", message, NULL);
+	_exit(1);
+}
+
+static void cope_with_gdb_brokenness()
+{
+	static gboolean already_done = FALSE;
+	char s[300], e[300];
+	int n;
+	pid_t ppid;
 
-	if (!inet_aton(host, &sin.sin_addr)) {
+#ifdef __linux__
+	if(already_done)
+		return;
+	already_done = TRUE;
+	ppid = getppid();
+	sprintf(s,"/proc/%d/exe", ppid);
+	n = readlink(s, e, sizeof(e));
+	e[MAX(n,sizeof(e)-1)] = '\0';
+	
+	if(strstr(e,"gdb")) {
+		debug_printf("Debugger detected, performing useless query...\n");
+		gethostbyname("x.x.x.x.x");
+	}
+#endif
+}
+
+int gaim_gethostbyname_async(const char *hostname, int port, int socktype, dns_callback_t callback, gpointer data)
+{
+	pending_dns_request_t *req = NULL;
+	dns_params_t dns_params;
+	
+	strncpy(dns_params.hostname, hostname, sizeof(dns_params.hostname)-1);
+	dns_params.hostname[sizeof(dns_params.hostname)-1] = '\0';
+	dns_params.port = port;
+	dns_params.socktype = socktype;
+	
+	/* Is there a free available child? */
+	while(free_dns_children && !req) {
+		GSList *l = free_dns_children;
+		free_dns_children = g_slist_remove_link(free_dns_children, l);
+		req = l->data;
+		g_slist_free(l);
+		
+		if(send_dns_request_to_child(req, &dns_params) != 0) {
+			g_free(req);
+			req = NULL;
+			number_of_dns_children--;
+			continue;
+		}
+		
+	}
+
+	if(!req) {
+		int child_out[2], child_in[2];
+
+		if(number_of_dns_children >= MAX_DNS_CHILDREN) {
+			queued_dns_request_t *r = g_new(queued_dns_request_t, 1);
+			memcpy(&(r->params), &dns_params, sizeof(dns_params));
+			r->callback = callback;
+			r->data = data;
+			if(!queued_requests)
+				queued_requests = g_queue_new();
+			g_queue_push_tail(queued_requests, r);
+			debug_printf("DNS query for '%s' queued\n", hostname);
+			return 0;
+		}
+
+		if(pipe(child_out) || pipe(child_in)) {
+			debug_printf("Could not create pipes: %s",strerror(errno));
+			return -1;
+		}
+
+		/* We need to create a new child. */
+		req = g_new(pending_dns_request_t,1);
+		
+		cope_with_gdb_brokenness();
+	
+		req->dns_pid=fork();
+		if(req->dns_pid==0) {
+			const int zero = 0;
+#ifdef HAVE_GETADDRINFO
+			struct addrinfo hints, *res;
+			char servname[20];
+			int rc;
+#else
+			struct sockaddr_in sin;
+			const socklen_t addrlen = sizeof(sin);
+#endif
+#ifdef HAVE_SIGNAL_H
+			signal(SIGHUP, SIG_DFL);
+			signal(SIGINT, SIG_DFL);
+			signal(SIGQUIT, SIG_DFL);
+			signal(SIGCHLD, SIG_DFL);
+			signal(SIGTERM, SIG_DFL);
+			signal(SIGTRAP, trap_gdb_bug);
+#endif
+
+
+			close(child_out[0]);
+			close(child_in[1]);
+
+			while(1) {
+				if(dns_params.hostname[0] == '\0') {
+					const char Y = 'Y';
+					fd_set fds;
+					struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 };
+					FD_ZERO(&fds);
+					FD_SET(child_in[0], &fds);
+					rc = select(child_in[0]+1, &fds, NULL, NULL, &tv);
+					if(!rc) {
+						if(opt_debug)
+							fprintf(stderr,"dns[%d]: nobody needs me... =(\n", getpid());
+						break;
+					}
+					rc = read(child_in[0], &dns_params, sizeof(dns_params));
+					if(rc < 0) {
+						perror("read()");
+						break;
+					}
+					if(rc==0) {
+						if(opt_debug)
+							fprintf(stderr,"dns[%d]: Ops, father has gone, wait for me, wait...!\n", getpid());
+						_exit(0);
+					}
+					if(dns_params.hostname[0] == '\0') {
+						fprintf(stderr, "dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port);
+						_exit(1);
+					}
+					write(child_out[1], &Y, 1);
+				}
+
+#ifdef HAVE_GETADDRINFO
+				g_snprintf(servname, sizeof(servname), "%d", dns_params.port);
+				memset(&hints,0,sizeof(hints));
+				hints.ai_socktype = dns_params.socktype;
+				rc = getaddrinfo(dns_params.hostname, servname, &hints, &res);
+				if(rc) {
+					write(child_out[1], &rc, sizeof(int));
+					close(child_out[1]);
+					if(opt_debug)
+						fprintf(stderr,"dns[%d] Error: getaddrinfo returned %d\n",
+							getpid(), rc);
+					dns_params.hostname[0] = '\0';
+					continue;
+				}
+				write(child_out[1], &zero, sizeof(zero));
+				write(child_out[1], &(res->ai_addrlen), sizeof(res->ai_addrlen));
+				write(child_out[1], res->ai_addr, res->ai_addrlen);
+				freeaddrinfo(res);
+#else
+				if (!inet_aton(hostname, &sin.sin_addr)) {
+					struct hostent *hp;
+					if(!(hp = gethostbyname(dns_params.hostname))) {
+						write(child_out[1], &h_errno, sizeof(int));
+						close(child_out[1]);
+						if(opt_debug)
+							fprintf(stderr,"DNS Error: %s\n",hstrerror(h_errno));
+						_exit(0);
+					}
+					memset(&sin, 0, sizeof(struct sockaddr_in));
+					memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
+					sin.sin_family = hp->h_addrtype;
+				} else
+					sin.sin_family = AF_INET;
+				sin.sin_port = htons(dns_params.port);
+				write(child_out[1], &zero, sizeof(zero));
+				write(child_out[1], &addrlen, sizeof(addrlen));
+				write(child_out[1], &sin, addrlen);
+#endif
+				dns_params.hostname[0] = '\0';
+			}
+			close(child_out[1]);
+			close(child_in[0]);
+			_exit(0);
+		}
+		close(child_out[1]);
+		close(child_in[0]);
+		if(req->dns_pid==-1) {
+			debug_printf("Could not create child process for DNS: %s\n",strerror(errno));
+			g_free(req);
+			return -1;
+		}
+		req->fd_in = child_in[1];
+		req->fd_out = child_out[0];
+		number_of_dns_children++;
+		debug_printf("Created new DNS child %d, there are now %d children.\n",
+			req->dns_pid, number_of_dns_children);
+	}
+	req->host=g_strdup(hostname);
+	req->port=port;
+	req->callback=callback;
+	req->data=data;
+	req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req);
+	return 0;
+}
+#else
+
+typedef struct {
+	gpointer data;
+	size_t addrlen;
+	struct sockaddr *addr;
+	dns_callback_t callback;
+} pending_dns_request_t;
+
+static gboolean host_resolved(gpointer data)
+{
+	pending_dns_request_t *req = (pending_dns_request_t*)data;
+	req->callback(req->addr, req->addrlen, req->data, NULL);
+	g_free(req->addr);
+	g_free(req);
+	return FALSE;
+}
+
+int gaim_gethostbyname_async(const char *hostname, int port, int socktype, dns_callback_t callback, gpointer data)
+{
+	struct sockaddr_in sin;
+	pending_dns_request_t *req;
+
+	if (!inet_aton(hostname, &sin.sin_addr)) {
 		struct hostent *hp;
-		if(!(hp = gethostbyname(host))) {
+		if(!(hp = gethostbyname(hostname))) {
 			debug_printf("gaim_gethostbyname(\"%s\", %d) failed: %s",
-				     host, port, hstrerror(h_errno));
-			return NULL;
+				     hostname, port, hstrerror(h_errno));
+			return -1;
 		}
 		memset(&sin, 0, sizeof(struct sockaddr_in));
 		memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
@@ -147,9 +549,17 @@
 		sin.sin_family = AF_INET;
 	sin.sin_port = htons(port);
 
-	return &sin;
+	req = g_new(pending_dns_request_t, 1);
+	req->addr = (struct sockaddr*) g_memdup(&sin, sizeof(sin));
+	req->addrlen = sizeof(sin);
+	req->data = data;
+	req->callback = callback;
+	g_timeout_add(10, host_resolved, req);
+	return 0;
 }
 
+#endif
+
 static void no_one_calls(gpointer data, gint source, GaimInputCondition cond)
 {
 	struct PHB *phb = data;
@@ -163,12 +573,14 @@
 		close(source);
 		gaim_input_remove(phb->inpa);
 		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
 		g_free(phb);
 		return;
 	}
 	fcntl(source, F_SETFL, 0);
 	gaim_input_remove(phb->inpa);
 	phb->func(phb->data, source, GAIM_INPUT_READ);
+	g_free(phb->host);
 	g_free(phb);
 }
 
@@ -177,39 +589,35 @@
 	struct PHB *phb = data;
 
 	phb->func(phb->data, phb->port, GAIM_INPUT_READ);
+	g_free(phb->host);
 	g_free(phb);
 
 	return FALSE;
 }
 
 
-static int proxy_connect_none(char *host, unsigned short port, struct PHB *phb)
+static int proxy_connect_none(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
 {
-	struct sockaddr_in *sin;
 	int fd = -1;
 
-	debug_printf("connecting to %s:%d with no proxy\n", host, port);
+	debug_printf("connecting to %s:%d with no proxy\n", phb->host, phb->port);
 
-	if (!(sin = gaim_gethostbyname(host, port))) {
-		debug_printf("gethostbyname failed\n");
-		g_free(phb);
-		return -1;
-	}
-
-	if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) {
-		debug_printf("unable to create socket\n");
+	if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) {
+		debug_printf("unable to create socket: %s\n", strerror(errno));
+		g_free(phb->host);
 		g_free(phb);
 		return -1;
 	}
 	fcntl(fd, F_SETFL, O_NONBLOCK);
 
-	if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) {
+	if (connect(fd, (struct sockaddr *)addr, addrlen) < 0) {
 		if ((errno == EINPROGRESS) || (errno == EINTR)) {
 			debug_printf("Connect would have blocked\n");
 			phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, no_one_calls, phb);
 		} else {
 			debug_printf("connect failed (errno %d)\n", errno);
 			close(fd);
+			g_free(phb->host);
 			g_free(phb);
 			return -1;
 		}
@@ -221,6 +629,7 @@
 		if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
 			debug_printf("getsockopt failed\n");
 			close(fd);
+			g_free(phb->host);
 			g_free(phb);
 			return -1;
 		}
@@ -241,8 +650,9 @@
 {
 	int nlc = 0;
 	int pos = 0;
+	int minor, major, status, error=0;
 	struct PHB *phb = data;
-	char inputline[8192];
+	char inputline[8192], *p;
 
 	gaim_input_remove(phb->inpa);
 
@@ -253,19 +663,35 @@
 			nlc = 0;
 	}
 	inputline[pos] = '\0';
-
-	debug_printf("Proxy says: %s\n", inputline);
-
-	if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) ||
-	    (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) {
-		phb->func(phb->data, source, GAIM_INPUT_READ);
-		g_free(phb->host);
-		g_free(phb);
-		return;
+	
+	error = strncmp(inputline, "HTTP/", 5) != 0;
+	if(!error) {
+		p = inputline + 5;
+		major = strtol(p, &p, 10);
+		error = (major==0) || (*p != '.');
+		if(!error) {
+			p++;
+			minor = strtol(p, &p, 10);
+			error = (*p!=' ');
+			if(!error) {
+				p++;
+				status = strtol(p, &p, 10);
+				error = (*p!=' ');
+			}
+		}
+	}
+	
+	if(error) {
+		debug_printf("Unable to parse proxy's response: %s\n", inputline);
+		close(source);
+		source=-1;
+	} else if(status!=200) {
+		debug_printf("Proxy reserver replied: (%s)\n", p);
+		close(source);
+		source=-1;
 	}
 
-	close(source);
-	phb->func(phb->data, -1, GAIM_INPUT_READ);
+	phb->func(phb->data, source, GAIM_INPUT_READ);
 	g_free(phb->host);
 	g_free(phb);
 	return;
@@ -273,7 +699,8 @@
 
 static void http_canwrite(gpointer data, gint source, GaimInputCondition cond)
 {
-	char cmd[384];
+	char request[8192];
+	int request_len = 0;
 	struct PHB *phb = data;
 	unsigned int len;
 	int error = ETIMEDOUT;
@@ -289,35 +716,24 @@
 		g_free(phb);
 		return;
 	}
-	fcntl(source, F_SETFL, 0);
-	g_snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port,
+	request_len = g_snprintf(request, sizeof(request), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port,
 		   phb->host, phb->port);
-	if (send(source, cmd, strlen(cmd), 0) < 0) {
-		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
 
 	if (proxyuser) {
 		char *t1, *t2;
 		t1 = g_strdup_printf("%s:%s", proxyuser, proxypass);
 		t2 = tobase64(t1);
 		g_free(t1);
-		g_snprintf(cmd, sizeof(cmd), "Proxy-Authorization: Basic %s\r\n", t2);
+		g_return_if_fail(request_len < sizeof(request));
+		request_len += g_snprintf(request + request_len, sizeof(request) - request_len, "Proxy-Authorization: Basic %s\r\n", t2);
 		g_free(t2);
-		if (send(source, cmd, strlen(cmd), 0) < 0) {
-			close(source);
-			phb->func(phb->data, -1, GAIM_INPUT_READ);
-			g_free(phb->host);
-			g_free(phb);
-			return;
-		}
 	}
 
-	g_snprintf(cmd, sizeof(cmd), "\r\n");
-	if (send(source, cmd, strlen(cmd), 0) < 0) {
+	g_return_if_fail(request_len < sizeof(request));
+	strcpy(request + request_len, "\r\n");
+	request_len += 2;
+
+	if (write(source, request, request_len) < 0) {
 		close(source);
 		phb->func(phb->data, -1, GAIM_INPUT_READ);
 		g_free(phb->host);
@@ -328,29 +744,21 @@
 	phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, http_canread, phb);
 }
 
-static int proxy_connect_http(char *host, unsigned short port, struct PHB *phb)
+static int proxy_connect_http(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
 {
-	struct sockaddr_in *sin;
 	int fd = -1;
 
-	debug_printf("connecting to %s:%d via %s:%d using HTTP\n", host, port, proxyhost, proxyport);
+	debug_printf("connecting to %s:%d via %s:%d using HTTP\n", phb->host, phb->port, proxyhost, proxyport);
 
-	if (!(sin = gaim_gethostbyname(proxyhost, proxyport))) {
+	if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) {
+		g_free(phb->host);
 		g_free(phb);
 		return -1;
 	}
 
-	if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) {
-		g_free(phb);
-		return -1;
-	}
-
-	phb->host = g_strdup(host);
-	phb->port = port;
-
 	fcntl(fd, F_SETFL, O_NONBLOCK);
 
-	if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) {
+	if (connect(fd, addr, addrlen) < 0) {
 		if ((errno == EINPROGRESS) || (errno == EINTR)) {
 			debug_printf("Connect would have blocked\n");
 			phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, http_canwrite, phb);
@@ -452,28 +860,20 @@
 	phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s4_canread, phb);
 }
 
-static int proxy_connect_socks4(char *host, unsigned short port, struct PHB *phb)
+static int proxy_connect_socks4(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
 {
-	struct sockaddr_in *sin;
 	int fd = -1;
 
-	debug_printf("connecting to %s:%d via %s:%d using SOCKS4\n", host, port, proxyhost, proxyport);
+	debug_printf("connecting to %s:%d via %s:%d using SOCKS4\n", phb->host, phb->port, proxyhost, proxyport);
 
-	if (!(sin = gaim_gethostbyname(proxyhost, proxyport))) {
+	if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) {
+		g_free(phb->host);
 		g_free(phb);
 		return -1;
 	}
 
-	if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) {
-		g_free(phb);
-		return -1;
-	}
-
-	phb->host = g_strdup(host);
-	phb->port = port;
-
 	fcntl(fd, F_SETFL, O_NONBLOCK);
-	if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) {
+	if (connect(fd, addr, addrlen) < 0) {
 		if ((errno == EINPROGRESS) || (errno == EINTR)) {
 			debug_printf("Connect would have blocked\n");
 			phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, s4_canwrite, phb);
@@ -678,28 +1078,20 @@
 	phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread, phb);
 }
 
-static int proxy_connect_socks5(char *host, unsigned short port, struct PHB *phb)
+static int proxy_connect_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
 {
-	struct sockaddr_in *sin;
 	int fd = -1;
 
-	debug_printf("connecting to %s:%d via %s:%d using SOCKS5\n", host, port, proxyhost, proxyport);
+	debug_printf("connecting to %s:%d via %s:%d using SOCKS5\n", phb->host, phb->port, proxyhost, proxyport);
 
-	if (!(sin = gaim_gethostbyname(proxyhost, proxyport))) {
+	if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) {
+		g_free(phb->host);
 		g_free(phb);
 		return -1;
 	}
 
-	if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) {
-		g_free(phb);
-		return -1;
-	}
-
-	phb->host = g_strdup(host);
-	phb->port = port;
-
 	fcntl(fd, F_SETFL, O_NONBLOCK);
-	if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) {
+	if (connect(fd, addr, addrlen) < 0) {
 		if ((errno == EINPROGRESS) || (errno == EINTR)) {
 			debug_printf("Connect would have blocked\n");
 			phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, s5_canwrite, phb);
@@ -728,29 +1120,70 @@
 	return fd;
 }
 
+static void connection_host_resolved(struct sockaddr *addr, size_t addrlen, gpointer data, const char *error_message)
+{
+	struct PHB *phb = (struct PHB*)data;
+	
+	if(!addr)
+	{
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		return;
+	}
+
+	switch(phb->proxytype)
+	{
+		case PROXY_NONE:
+			proxy_connect_none(phb, addr, addrlen);
+			break;
+		case PROXY_HTTP:
+			proxy_connect_http(phb, addr, addrlen);
+			break;
+		case PROXY_SOCKS4:
+			proxy_connect_socks4(phb, addr, addrlen);
+			break;
+		case PROXY_SOCKS5:
+			proxy_connect_socks5(phb, addr, addrlen);
+			break;
+	}
+}
+
 int proxy_connect(char *host, int port, GaimInputFunction func, gpointer data)
 {
+	char *connecthost = host;
+	int connectport = port;
 	struct PHB *phb = g_new0(struct PHB, 1);
 	phb->func = func;
 	phb->data = data;
+	phb->host = g_strdup(host);
+	phb->port = port;
+	phb->proxytype = proxytype;
 
 	if (!host || !port || (port == -1) || !func) {
+		if(host)
+			g_free(phb->host);
 		g_free(phb);
 		return -1;
 	}
-#ifndef _WIN32
-	sethostent(1);
-#endif
-	if ((proxytype == PROXY_NONE) || !proxyhost || !proxyhost[0] || !proxyport || (proxyport == -1))
-		return proxy_connect_none(host, port, phb);
-	else if (proxytype == PROXY_HTTP)
-		return proxy_connect_http(host, port, phb);
-	else if (proxytype == PROXY_SOCKS4)
-		return proxy_connect_socks4(host, port, phb);
-	else if (proxytype == PROXY_SOCKS5)
-		return proxy_connect_socks5(host, port, phb);
+
+	if ((phb->proxytype!=PROXY_NONE) && (!proxyhost || !proxyhost[0] || !proxyport || (proxyport == -1)))
+		phb->proxytype=PROXY_NONE;
 
-	g_free(phb);
-	return -1;
+	switch(phb->proxytype)
+	{
+		case PROXY_NONE:
+			break;
+		case PROXY_HTTP:
+		case PROXY_SOCKS4:
+		case PROXY_SOCKS5:
+			connecthost=proxyhost;
+			connectport=proxyport;
+			break;
+		default:
+			g_free(phb->host);
+			g_free(phb);
+			return -1;
+	}
+	
+	gaim_gethostbyname_async(connecthost, connectport, SOCK_STREAM, connection_host_resolved, phb);
+	return 1;
 }
-
--- a/src/proxy.h	Mon Jan 20 18:12:14 2003 +0000
+++ b/src/proxy.h	Mon Jan 20 19:26:56 2003 +0000
@@ -39,14 +39,16 @@
 
 #include <glib.h>
 
-#define PROXY_NONE 0
-#define PROXY_HTTP 1
-#define PROXY_SOCKS4 2
-#define PROXY_SOCKS5 3
+typedef enum {
+	PROXY_NONE = 0,
+	PROXY_HTTP,
+	PROXY_SOCKS4,
+	PROXY_SOCKS5,
+} proxytype_t;
 
 extern char proxyhost[128];
 extern int  proxyport;
-extern int  proxytype;
+extern proxytype_t  proxytype;
 extern char proxyuser[128];
 extern char proxypass[128];
 extern guint proxy_info_is_from_gaimrc;