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