Home | History | Annotate | Download | only in libcutils
      1 /*
      2 ** Copyright 2006, The Android Open Source Project
      3 **
      4 ** Licensed under the Apache License, Version 2.0 (the "License");
      5 ** you may not use this file except in compliance with the License.
      6 ** You may obtain a copy of the License at
      7 **
      8 **     http://www.apache.org/licenses/LICENSE-2.0
      9 **
     10 ** Unless required by applicable law or agreed to in writing, software
     11 ** distributed under the License is distributed on an "AS IS" BASIS,
     12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 ** See the License for the specific language governing permissions and
     14 ** limitations under the License.
     15 */
     16 
     17 #include <cutils/sockets.h>
     18 
     19 #include <errno.h>
     20 #include <fcntl.h>
     21 #include <stddef.h>
     22 #include <stdlib.h>
     23 #include <string.h>
     24 #include <unistd.h>
     25 
     26 #include <sys/socket.h>
     27 #include <sys/select.h>
     28 #include <sys/types.h>
     29 #include <netinet/in.h>
     30 #include <netdb.h>
     31 
     32 static int toggle_O_NONBLOCK(int s) {
     33     int flags = fcntl(s, F_GETFL);
     34     if (flags == -1 || fcntl(s, F_SETFL, flags ^ O_NONBLOCK) == -1) {
     35         close(s);
     36         return -1;
     37     }
     38     return s;
     39 }
     40 
     41 // Connect to the given host and port.
     42 // 'timeout' is in seconds (0 for no timeout).
     43 // Returns a file descriptor or -1 on error.
     44 // On error, check *getaddrinfo_error (for use with gai_strerror) first;
     45 // if that's 0, use errno instead.
     46 int socket_network_client_timeout(const char* host, int port, int type, int timeout,
     47                                   int* getaddrinfo_error) {
     48     struct addrinfo hints;
     49     memset(&hints, 0, sizeof(hints));
     50     hints.ai_family = AF_UNSPEC;
     51     hints.ai_socktype = type;
     52 
     53     char port_str[16];
     54     snprintf(port_str, sizeof(port_str), "%d", port);
     55 
     56     struct addrinfo* addrs;
     57     *getaddrinfo_error = getaddrinfo(host, port_str, &hints, &addrs);
     58     if (*getaddrinfo_error != 0) {
     59         return -1;
     60     }
     61 
     62     int result = -1;
     63     for (struct addrinfo* addr = addrs; addr != NULL; addr = addr->ai_next) {
     64         // The Mac doesn't have SOCK_NONBLOCK.
     65         int s = socket(addr->ai_family, type, addr->ai_protocol);
     66         if (s == -1 || toggle_O_NONBLOCK(s) == -1) break;
     67 
     68         int rc = connect(s, addr->ai_addr, addr->ai_addrlen);
     69         if (rc == 0) {
     70             result = toggle_O_NONBLOCK(s);
     71             break;
     72         } else if (rc == -1 && errno != EINPROGRESS) {
     73             close(s);
     74             continue;
     75         }
     76 
     77         fd_set r_set;
     78         FD_ZERO(&r_set);
     79         FD_SET(s, &r_set);
     80         fd_set w_set = r_set;
     81 
     82         struct timeval ts;
     83         ts.tv_sec = timeout;
     84         ts.tv_usec = 0;
     85         if ((rc = select(s + 1, &r_set, &w_set, NULL, (timeout != 0) ? &ts : NULL)) == -1) {
     86             close(s);
     87             break;
     88         }
     89         if (rc == 0) {  // we had a timeout
     90             errno = ETIMEDOUT;
     91             close(s);
     92             break;
     93         }
     94 
     95         int error = 0;
     96         socklen_t len = sizeof(error);
     97         if (FD_ISSET(s, &r_set) || FD_ISSET(s, &w_set)) {
     98             if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
     99                 close(s);
    100                 break;
    101             }
    102         } else {
    103             close(s);
    104             break;
    105         }
    106 
    107         if (error) {  // check if we had a socket error
    108             // TODO: Update the timeout.
    109             errno = error;
    110             close(s);
    111             continue;
    112         }
    113 
    114         result = toggle_O_NONBLOCK(s);
    115         break;
    116     }
    117 
    118     freeaddrinfo(addrs);
    119     return result;
    120 }
    121 
    122 int socket_network_client(const char* host, int port, int type) {
    123     int getaddrinfo_error;
    124     return socket_network_client_timeout(host, port, type, 0, &getaddrinfo_error);
    125 }
    126