Changeset 762d96f for lib/proxy.c


Ignore:
Timestamp:
2010-08-15T22:07:57Z (9 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
Branches:
master
Children:
d20ea9f
Parents:
136c2bb
Message:

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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • lib/proxy.c

    r136c2bb r762d96f  
    4949char proxypass[128] = "";
    5050
     51/* Some systems don't know this one. It's not essential, so set it to 0 then. */
     52#ifndef AI_NUMERICSERV
     53#define AI_NUMERICSERV 0
     54#endif
     55
    5156struct PHB {
    5257        b_event_handler func, proxy_func;
     
    5661        int fd;
    5762        gint inpa;
     63        struct addrinfo *gai, *gai_cur;
    5864};
     65
     66static int proxy_connect_none(const char *host, unsigned short port_, struct PHB *phb);
    5967
    6068static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition cond)
     
    6674       
    6775#ifndef _WIN32
    68         if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
     76        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0 || error) {
     77                if ((phb->gai_cur = phb->gai_cur->ai_next)) {
     78                        int new_fd;
     79                        b_event_remove(phb->inpa);
     80                        if ((new_fd = proxy_connect_none(NULL, 0, phb))) {
     81                                b_event_remove(phb->inpa);
     82                                closesocket(source);
     83                                dup2(new_fd, source);
     84                                phb->inpa = b_input_add(source, B_EV_IO_WRITE, gaim_io_connected, phb);
     85                                return FALSE;
     86                        }
     87                }
     88                freeaddrinfo(phb->gai);
    6989                closesocket(source);
    7090                b_event_remove(phb->inpa);
     
    7898        }
    7999#endif
     100        freeaddrinfo(phb->gai);
    80101        sock_make_blocking(source);
    81102        b_event_remove(phb->inpa);
     
    94115        struct sockaddr_in me;
    95116        int fd = -1;
    96         int ret;
    97         char port[6];
    98         struct addrinfo hints;
    99         struct addrinfo* result;
    100 
    101         g_snprintf(port, sizeof(port), "%d", port_);
    102 
    103         memset(&hints, 0, sizeof(struct addrinfo));
    104         hints.ai_family = AF_UNSPEC;
    105         hints.ai_socktype = SOCK_STREAM;
    106         hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
    107 
    108         if (!(ret = getaddrinfo(host, port, &hints, &result)))
     117       
     118        if (phb->gai_cur == NULL)
    109119        {
    110                 struct addrinfo* rp;
    111 
    112                 for (rp = result; rp; rp = rp->ai_next)
     120                int ret;
     121                char port[6];
     122                struct addrinfo hints;
     123       
     124                g_snprintf(port, sizeof(port), "%d", port_);
     125       
     126                memset(&hints, 0, sizeof(struct addrinfo));
     127                hints.ai_family = AF_UNSPEC;
     128                hints.ai_socktype = SOCK_STREAM;
     129                hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
     130       
     131                if (!(ret = getaddrinfo(host, port, &hints, &phb->gai)))
     132                        phb->gai_cur = phb->gai;
     133                else
     134                        event_debug("gai(): %s\n", gai_strerror(ret));
     135        }
     136       
     137        for (; phb->gai_cur; phb->gai_cur = phb->gai_cur->ai_next)
     138        {
     139                if ((fd = socket(phb->gai_cur->ai_family, phb->gai_cur->ai_socktype, phb->gai_cur->ai_protocol)) < 0) {
     140                        event_debug( "socket failed: %d\n", errno);
     141                        continue;
     142                }
     143
     144                sock_make_nonblocking(fd);
     145
     146                if (global.conf->iface_out)
    113147                {
    114                         if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0) {
    115                                 event_debug( "socket failed: %d\n", errno);
    116                                 continue;
    117                         }
    118 
    119                         sock_make_nonblocking(fd);
    120 
    121                         if (global.conf->iface_out)
    122                         {
    123                                 me.sin_family = AF_INET;
    124                                 me.sin_port = 0;
    125                                 me.sin_addr.s_addr = inet_addr( global.conf->iface_out );
     148                        me.sin_family = AF_INET;
     149                        me.sin_port = 0;
     150                        me.sin_addr.s_addr = inet_addr( global.conf->iface_out );
    126151                               
    127                                 if (bind(fd, (struct sockaddr *) &me, sizeof(me)) != 0)
    128                                         event_debug("bind( %d, \"%s\" ) failure\n", fd, global.conf->iface_out);
    129                         }
    130 
    131                         event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd);
    132        
    133                         if (connect(fd, rp->ai_addr, rp->ai_addrlen) < 0 && !sockerr_again()) {
    134                                 event_debug( "connect failed: %s\n", strerror(errno));
    135                                 closesocket(fd);
    136                                 fd = -1;
    137                                 continue;
    138                         } else {
    139                                 phb->inpa = b_input_add(fd, B_EV_IO_WRITE, gaim_io_connected, phb);
    140                                 phb->fd = fd;
    141                                
    142                                 break;
    143                         }
    144                 }
    145 
    146                 freeaddrinfo(result);
    147         }
    148         else
    149         {
    150                 event_debug("gai(): %s\n", gai_strerror(ret));
    151         }
    152        
    153         if(fd < 0)
     152                        if (bind(fd, (struct sockaddr *) &me, sizeof(me)) != 0)
     153                                event_debug("bind( %d, \"%s\" ) failure\n", fd, global.conf->iface_out);
     154                }
     155
     156                event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd);
     157       
     158                if (connect(fd, phb->gai_cur->ai_addr, phb->gai_cur->ai_addrlen) < 0 && !sockerr_again()) {
     159                        event_debug( "connect failed: %s\n", strerror(errno));
     160                        closesocket(fd);
     161                        fd = -1;
     162                        continue;
     163                } else {
     164                        phb->inpa = b_input_add(fd, B_EV_IO_WRITE, gaim_io_connected, phb);
     165                        phb->fd = fd;
     166                       
     167                        break;
     168                }
     169        }
     170       
     171        if(fd < 0 && host)
    154172                g_free(phb);
    155173
Note: See TracChangeset for help on using the changeset viewer.