aboutsummaryrefslogtreecommitdiffstats
path: root/lib/proxy.c
diff options
context:
space:
mode:
authorWilmer van der Gaast <wilmer@gaast.net>2010-08-15 23:07:57 +0100
committerWilmer van der Gaast <wilmer@gaast.net>2010-08-15 23:07:57 +0100
commit762d96f3bc665dd97b037abde59243c3db8da755 (patch)
tree7a696a8dd3c0169a3f2acb90d736bfa073409be7 /lib/proxy.c
parent136c2bb632715ab83710c93c7b339c5cca7d2679 (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.c118
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;