diff options
| author | Wilmer van der Gaast <wilmer@gaast.net> | 2010-08-15 23:07:57 +0100 | 
|---|---|---|
| committer | Wilmer van der Gaast <wilmer@gaast.net> | 2010-08-15 23:07:57 +0100 | 
| commit | 762d96f3bc665dd97b037abde59243c3db8da755 (patch) | |
| tree | 7a696a8dd3c0169a3f2acb90d736bfa073409be7 /lib/proxy.c | |
| parent | 136c2bb632715ab83710c93c7b339c5cca7d2679 (diff) | |
If a connection fails, try the next address from the getaddrinfo() results.
This should fix issues with hosts that have IPv6 and IPv4 addresses but listen
on only one of them. (Bug #673)
This also fixes a bug that broke error checking in gaim_io_connected(), until
now event handlers were never actually getting proper error reporting (fd=-1),
but IIRC they should all handle it anyway as I was never aware of this bug.
Diffstat (limited to 'lib/proxy.c')
| -rw-r--r-- | lib/proxy.c | 118 | 
1 files changed, 68 insertions, 50 deletions
| diff --git a/lib/proxy.c b/lib/proxy.c index 0b1866ea..e7d35351 100644 --- a/lib/proxy.c +++ b/lib/proxy.c @@ -48,6 +48,11 @@ int proxytype = PROXY_NONE;  char proxyuser[128] = "";  char proxypass[128] = ""; +/* Some systems don't know this one. It's not essential, so set it to 0 then. */ +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0 +#endif +  struct PHB {  	b_event_handler func, proxy_func;  	gpointer data, proxy_data; @@ -55,8 +60,11 @@ struct PHB {  	int port;  	int fd;  	gint inpa; +	struct addrinfo *gai, *gai_cur;  }; +static int proxy_connect_none(const char *host, unsigned short port_, struct PHB *phb); +  static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition cond)  {  	struct PHB *phb = data; @@ -65,7 +73,19 @@ static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition  	len = sizeof(error);  #ifndef _WIN32 -	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { +	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0 || error) { +		if ((phb->gai_cur = phb->gai_cur->ai_next)) { +			int new_fd; +			b_event_remove(phb->inpa); +			if ((new_fd = proxy_connect_none(NULL, 0, phb))) { +				b_event_remove(phb->inpa); +				closesocket(source); +				dup2(new_fd, source); +				phb->inpa = b_input_add(source, B_EV_IO_WRITE, gaim_io_connected, phb); +				return FALSE; +			} +		} +		freeaddrinfo(phb->gai);  		closesocket(source);  		b_event_remove(phb->inpa);  		if( phb->proxy_func ) @@ -77,6 +97,7 @@ static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition  		return FALSE;  	}  #endif +	freeaddrinfo(phb->gai);  	sock_make_blocking(source);  	b_event_remove(phb->inpa);  	if( phb->proxy_func ) @@ -93,64 +114,61 @@ static int proxy_connect_none(const char *host, unsigned short port_, struct PHB  {  	struct sockaddr_in me;  	int fd = -1; -	int ret; -	char port[6]; -	struct addrinfo hints; -	struct addrinfo* result; - -	g_snprintf(port, sizeof(port), "%d", port_); - -	memset(&hints, 0, sizeof(struct addrinfo)); -	hints.ai_family = AF_UNSPEC; -	hints.ai_socktype = SOCK_STREAM; -	hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; - -	if (!(ret = getaddrinfo(host, port, &hints, &result))) +	 +	if (phb->gai_cur == NULL)  	{ -		struct addrinfo* rp; - -		for (rp = result; rp; rp = rp->ai_next) -		{ -			if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0) { -				event_debug( "socket failed: %d\n", errno); -				continue; -			} +		int ret; +		char port[6]; +		struct addrinfo hints; +	 +		g_snprintf(port, sizeof(port), "%d", port_); +	 +		memset(&hints, 0, sizeof(struct addrinfo)); +		hints.ai_family = AF_UNSPEC; +		hints.ai_socktype = SOCK_STREAM; +		hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; +	 +		if (!(ret = getaddrinfo(host, port, &hints, &phb->gai))) +			phb->gai_cur = phb->gai; +		else +			event_debug("gai(): %s\n", gai_strerror(ret)); +	} +	 +	for (; phb->gai_cur; phb->gai_cur = phb->gai_cur->ai_next) +	{ +		if ((fd = socket(phb->gai_cur->ai_family, phb->gai_cur->ai_socktype, phb->gai_cur->ai_protocol)) < 0) { +			event_debug( "socket failed: %d\n", errno); +			continue; +		} -			sock_make_nonblocking(fd); +		sock_make_nonblocking(fd); -			if (global.conf->iface_out) -			{ -				me.sin_family = AF_INET; -				me.sin_port = 0; -				me.sin_addr.s_addr = inet_addr( global.conf->iface_out ); +		if (global.conf->iface_out) +		{ +			me.sin_family = AF_INET; +			me.sin_port = 0; +			me.sin_addr.s_addr = inet_addr( global.conf->iface_out ); -				if (bind(fd, (struct sockaddr *) &me, sizeof(me)) != 0) -					event_debug("bind( %d, \"%s\" ) failure\n", fd, global.conf->iface_out); -			} +			if (bind(fd, (struct sockaddr *) &me, sizeof(me)) != 0) +				event_debug("bind( %d, \"%s\" ) failure\n", fd, global.conf->iface_out); +		} -			event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd); +		event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd); -			if (connect(fd, rp->ai_addr, rp->ai_addrlen) < 0 && !sockerr_again()) { -				event_debug( "connect failed: %s\n", strerror(errno)); -				closesocket(fd); -				fd = -1; -				continue; -			} else { -				phb->inpa = b_input_add(fd, B_EV_IO_WRITE, gaim_io_connected, phb); -				phb->fd = fd; -				 -				break; -			} +		if (connect(fd, phb->gai_cur->ai_addr, phb->gai_cur->ai_addrlen) < 0 && !sockerr_again()) { +			event_debug( "connect failed: %s\n", strerror(errno)); +			closesocket(fd); +			fd = -1; +			continue; +		} else { +			phb->inpa = b_input_add(fd, B_EV_IO_WRITE, gaim_io_connected, phb); +			phb->fd = fd; +			 +			break;  		} - -		freeaddrinfo(result); -	} -	else -	{ -		event_debug("gai(): %s\n", gai_strerror(ret));  	} -	if(fd < 0) +	if(fd < 0 && host)  		g_free(phb);  	return fd; | 
