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 <errno.h>
     18 #include <fcntl.h>
     19 #include <stddef.h>
     20 #include <stdlib.h>
     21 #include <string.h>
     22 #include <unistd.h>
     23 
     24 #include <sys/socket.h>
     25 #include <sys/select.h>
     26 #include <sys/types.h>
     27 #include <netinet/in.h>
     28 #include <netdb.h>
     29 
     30 #include <cutils/sockets.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     // TODO: try all the addresses if there's more than one?
     63     int family = addrs[0].ai_family;
     64     int protocol = addrs[0].ai_protocol;
     65     socklen_t addr_len = addrs[0].ai_addrlen;
     66     struct sockaddr_storage addr;
     67     memcpy(&addr, addrs[0].ai_addr, addr_len);
     68 
     69     freeaddrinfo(addrs);
     70 
     71     // The Mac doesn't have SOCK_NONBLOCK.
     72     int s = socket(family, type, protocol);
     73     if (s == -1 || toggle_O_NONBLOCK(s) == -1) return -1;
     74 
     75     int rc = connect(s, (const struct sockaddr*) &addr, addr_len);
     76     if (rc == 0) {
     77         return toggle_O_NONBLOCK(s);
     78     } else if (rc == -1 && errno != EINPROGRESS) {
     79         close(s);
     80         return -1;
     81     }
     82 
     83     fd_set r_set;
     84     FD_ZERO(&r_set);
     85     FD_SET(s, &r_set);
     86     fd_set w_set = r_set;
     87 
     88     struct timeval ts;
     89     ts.tv_sec = timeout;
     90     ts.tv_usec = 0;
     91     if ((rc = select(s + 1, &r_set, &w_set, NULL, (timeout != 0) ? &ts : NULL)) == -1) {
     92         close(s);
     93         return -1;
     94     }
     95     if (rc == 0) {   // we had a timeout
     96         errno = ETIMEDOUT;
     97         close(s);
     98         return -1;
     99     }
    100 
    101     int error = 0;
    102     socklen_t len = sizeof(error);
    103     if (FD_ISSET(s, &r_set) || FD_ISSET(s, &w_set)) {
    104         if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
    105             close(s);
    106             return -1;
    107         }
    108     } else {
    109         close(s);
    110         return -1;
    111     }
    112 
    113     if (error) {  // check if we had a socket error
    114         errno = error;
    115         close(s);
    116         return -1;
    117     }
    118 
    119     return toggle_O_NONBLOCK(s);
    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