Mark Salyzyn | 1271716 | 2014-04-29 15:49:14 -0700 | [diff] [blame] | 1 | /* |
The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 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 | |
Elliott Hughes | 8e9aeb9 | 2017-11-10 10:22:07 -0800 | [diff] [blame] | 17 | #include <cutils/sockets.h> |
| 18 | |
Mark Salyzyn | 1271716 | 2014-04-29 15:49:14 -0700 | [diff] [blame] | 19 | #include <errno.h> |
Ken Lierman | aecc6a6 | 2013-06-20 09:27:13 -0700 | [diff] [blame] | 20 | #include <fcntl.h> |
Mark Salyzyn | 1271716 | 2014-04-29 15:49:14 -0700 | [diff] [blame] | 21 | #include <stddef.h> |
The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 22 | #include <stdlib.h> |
| 23 | #include <string.h> |
| 24 | #include <unistd.h> |
The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 25 | |
The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 26 | #include <sys/socket.h> |
| 27 | #include <sys/select.h> |
| 28 | #include <sys/types.h> |
| 29 | #include <netinet/in.h> |
| 30 | #include <netdb.h> |
The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 31 | |
Elliott Hughes | 0f8f70b | 2015-07-18 10:47:59 -0700 | [diff] [blame] | 32 | static int toggle_O_NONBLOCK(int s) { |
Elliott Hughes | 94ae418 | 2015-07-17 15:54:09 -0700 | [diff] [blame] | 33 | int flags = fcntl(s, F_GETFL); |
Elliott Hughes | 0f8f70b | 2015-07-18 10:47:59 -0700 | [diff] [blame] | 34 | if (flags == -1 || fcntl(s, F_SETFL, flags ^ O_NONBLOCK) == -1) { |
Elliott Hughes | 94ae418 | 2015-07-17 15:54:09 -0700 | [diff] [blame] | 35 | close(s); |
| 36 | return -1; |
| 37 | } |
Elliott Hughes | 94ae418 | 2015-07-17 15:54:09 -0700 | [diff] [blame] | 38 | return s; |
Ken Lierman | aecc6a6 | 2013-06-20 09:27:13 -0700 | [diff] [blame] | 39 | } |
| 40 | |
Elliott Hughes | 94ae418 | 2015-07-17 15:54:09 -0700 | [diff] [blame] | 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. |
Elliott Hughes | 381cfa9 | 2015-07-23 17:12:58 -0700 | [diff] [blame] | 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) { |
Elliott Hughes | 94ae418 | 2015-07-17 15:54:09 -0700 | [diff] [blame] | 48 | struct addrinfo hints; |
| 49 | memset(&hints, 0, sizeof(hints)); |
| 50 | hints.ai_family = AF_UNSPEC; |
| 51 | hints.ai_socktype = type; |
Ken Lierman | aecc6a6 | 2013-06-20 09:27:13 -0700 | [diff] [blame] | 52 | |
Elliott Hughes | 94ae418 | 2015-07-17 15:54:09 -0700 | [diff] [blame] | 53 | char port_str[16]; |
| 54 | snprintf(port_str, sizeof(port_str), "%d", port); |
| 55 | |
| 56 | struct addrinfo* addrs; |
Elliott Hughes | 381cfa9 | 2015-07-23 17:12:58 -0700 | [diff] [blame] | 57 | *getaddrinfo_error = getaddrinfo(host, port_str, &hints, &addrs); |
Elliott Hughes | eabe8af | 2015-07-24 18:54:02 -0700 | [diff] [blame] | 58 | if (*getaddrinfo_error != 0) { |
Elliott Hughes | 94ae418 | 2015-07-17 15:54:09 -0700 | [diff] [blame] | 59 | return -1; |
| 60 | } |
| 61 | |
Josh Gao | 78cc20f | 2016-09-01 14:54:18 -0700 | [diff] [blame] | 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); |
Elliott Hughes | 3ff453a | 2017-08-02 12:57:02 -0700 | [diff] [blame] | 66 | if (s == -1 || toggle_O_NONBLOCK(s) == -1) break; |
Josh Gao | 78cc20f | 2016-09-01 14:54:18 -0700 | [diff] [blame] | 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); |
Tao Wu | 9b7341f | 2016-09-21 15:41:05 -0700 | [diff] [blame] | 115 | break; |
Josh Gao | 78cc20f | 2016-09-01 14:54:18 -0700 | [diff] [blame] | 116 | } |
Elliott Hughes | 94ae418 | 2015-07-17 15:54:09 -0700 | [diff] [blame] | 117 | |
| 118 | freeaddrinfo(addrs); |
Josh Gao | 78cc20f | 2016-09-01 14:54:18 -0700 | [diff] [blame] | 119 | return result; |
Elliott Hughes | 94ae418 | 2015-07-17 15:54:09 -0700 | [diff] [blame] | 120 | } |
The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 121 | |
Elliott Hughes | 94ae418 | 2015-07-17 15:54:09 -0700 | [diff] [blame] | 122 | int socket_network_client(const char* host, int port, int type) { |
Elliott Hughes | 381cfa9 | 2015-07-23 17:12:58 -0700 | [diff] [blame] | 123 | int getaddrinfo_error; |
| 124 | return socket_network_client_timeout(host, port, type, 0, &getaddrinfo_error); |
The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 125 | } |