Search code examples
phpcsocketsglibc

Where in php it's implemented that `socket_connect` iterates over multiple IP addresses returned for a given hostname?


I have a code like this:

if (!socket_connect($this->sock, $this->host, $this->port)) {

where $this->host is a hostname, that is resolved into 2 IP addresses.

If I strace this code I see something like

1456868407.567615 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
1456868407.567805 fcntl(3, F_GETFL)     = 0x2 (flags O_RDWR)
1456868407.568081 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
1456868407.568264 connect(3, {sa_family=AF_INET, sin_port=htons(5672), sin_addr=inet_addr("192.168.1.111")}, 16) = -1 EINPROGRESS (Operation now in progress)
1456868407.568763 poll([{fd=3, events=POLLIN|POLLOUT|POLLERR|POLLHUP}], 1, 3000) = 1 ([{fd=3, revents=POLLIN|POLLOUT|POLLERR|POLLHUP}])
1456868408.724034 getsockopt(3, SOL_SOCKET, SO_ERROR, [111], [4]) = 0
1456868408.724699 fcntl(3, F_SETFL, O_RDWR) = 0
1456868408.725414 close(3)              = 0
1456868408.725901 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
1456868408.726408 fcntl(3, F_GETFL)     = 0x2 (flags O_RDWR)
1456868408.727032 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
1456868408.727727 connect(3, {sa_family=AF_INET, sin_port=htons(5672), sin_addr=inet_addr("192.168.1.112")}, 16) = -1 EINPROGRESS (Operation now in progress)
1456868408.728484 poll([{fd=3, events=POLLIN|POLLOUT|POLLERR|POLLHUP}], 1, 1842) = 1 ([{fd=3, revents=POLLOUT}])
1456868408.729281 getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
1456868408.729590 fcntl(3, F_SETFL, O_RDWR) = 0

So after the first address 192.168.1.111 returned ECONNREFUSED (111) for the connection - php tries the next address.

I checked the corresponding php source code for the version I'm dealing with: https://github.com/php/php-src/blob/php-5.5.9/ext/sockets/sockets.c#L1376 and have not found any place where it loops:

  1. It resolves the domain name via if (! php_set_inet_addr(&sin, addr, php_sock TSRMLS_CC)) {

  2. Which in turn only sets the first address from the returned hostent structure memcpy(&(sin->sin_addr.s_addr), host_entry->h_addr_list[0], host_entry->h_length); at https://github.com/php/php-src/blob/php-5.5.9/ext%2Fsockets%2Fsockaddr_conv.c#L106

So my question is: where is the corresponding code implemented?

Or is it not php that does that but glibc? If so - where is it in the glibc sources? (I also tried to find there but failed)


Solution

  • Found it: https://github.com/php/php-src/blob/php-5.5.9/main/network.c#L797

    for (sal = psal; !fatal && *sal != NULL; sal++) {
        sa = *sal;
    
        /* create a socket for this address */
        sock = socket(sa->sa_family, socktype, 0);
    
        if (sock == SOCK_ERR) {
            continue;
        }
    
        // <skipped>
    
            n = php_network_connect_socket(sock, sa, socklen, asynchronous,
                        timeout ? &working_timeout : NULL,
                        error_string, error_code);
    
            if (n != -1) { // <-- here is the check
                goto connected;
            }
    
      // skipped
    
    }
    

    here it iterates over addresses until can connect to one.