diff src/proxy.c @ 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 43e396e94095
children 101fa831f52f
line wrap: on
line diff
--- 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;
 }
-