1 /* 2 * usock - socket helper functions 3 * 4 * Copyright (C) 2010 Steven Barth <steven@midlink.org> 5 * Copyright (C) 2011-2012 Felix Fietkau <nbd@openwrt.org> 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 #include <sys/types.h> 20 #include <sys/socket.h> 21 #include <sys/un.h> 22 #include <netdb.h> 23 #include <poll.h> 24 #include <stdlib.h> 25 #include <unistd.h> 26 #include <fcntl.h> 27 #include <errno.h> 28 #include <string.h> 29 #include <stdbool.h> 30 #include <stdint.h> 31 #include <stdio.h> 32 #include <limits.h> 33 34 #include "usock.h" 35 #include "utils.h" 36 37 static void usock_set_flags(int sock, unsigned int type) 38 { 39 int flags; 40 41 if (!(type & USOCK_NOCLOEXEC)) { 42 flags = fcntl(sock, F_GETFD); 43 if (flags >= 0) 44 fcntl(sock, F_SETFD, flags | FD_CLOEXEC); 45 } 46 47 if (type & USOCK_NONBLOCK) { 48 flags = fcntl(sock, F_GETFL); 49 if (flags >= 0) 50 fcntl(sock, F_SETFL, flags | O_NONBLOCK); 51 } 52 } 53 54 static int usock_connect(int type, struct sockaddr *sa, int sa_len, int family, int socktype, bool server) 55 { 56 int sock; 57 58 sock = socket(family, socktype, 0); 59 if (sock < 0) 60 return -1; 61 62 usock_set_flags(sock, type); 63 64 if (server) { 65 const int one = 1; 66 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); 67 68 if (!bind(sock, sa, sa_len) && 69 (socktype != SOCK_STREAM || !listen(sock, SOMAXCONN))) 70 return sock; 71 } else { 72 if (!connect(sock, sa, sa_len) || errno == EINPROGRESS) 73 return sock; 74 } 75 76 close(sock); 77 return -1; 78 } 79 80 static int usock_unix(int type, const char *host) 81 { 82 struct sockaddr_un sun = {.sun_family = AF_UNIX}; 83 bool server = !!(type & USOCK_SERVER); 84 int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; 85 86 if (strlen(host) >= sizeof(sun.sun_path)) { 87 errno = EINVAL; 88 return -1; 89 } 90 strcpy(sun.sun_path, host); 91 92 return usock_connect(type, (struct sockaddr*)&sun, sizeof(sun), AF_UNIX, socktype, server); 93 } 94 95 static int 96 usock_inet_notimeout(int type, struct addrinfo *result, void *addr) 97 { 98 struct addrinfo *rp; 99 int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; 100 bool server = !!(type & USOCK_SERVER); 101 int sock; 102 103 for (rp = result; rp != NULL; rp = rp->ai_next) { 104 sock = usock_connect(type, rp->ai_addr, rp->ai_addrlen, rp->ai_family, socktype, server); 105 if (sock >= 0) { 106 if (addr) 107 memcpy(addr, rp->ai_addr, rp->ai_addrlen); 108 return sock; 109 } 110 } 111 112 return -1; 113 } 114 115 static int poll_restart(struct pollfd *fds, int nfds, int timeout) 116 { 117 struct timespec ts, cur; 118 int64_t remaining; 119 int msec = timeout % 1000; 120 int ret; 121 122 clock_gettime(CLOCK_MONOTONIC, &ts); 123 124 ts.tv_nsec += msec * 1000000; 125 if (ts.tv_nsec >= 1000000000) { 126 ts.tv_sec++; 127 ts.tv_nsec -= 1000000000; 128 } 129 ts.tv_sec += timeout / 1000; 130 131 while (1) { 132 ret = poll(fds, nfds, timeout); 133 if (ret >= 0 || (errno != EINTR && errno != EAGAIN)) 134 return ret; 135 136 clock_gettime(CLOCK_MONOTONIC, &cur); 137 remaining = ((int64_t)ts.tv_sec - (int64_t)cur.tv_sec) * 1000; 138 remaining += (ts.tv_nsec - cur.tv_nsec) / 1000000; 139 if (remaining <= 0) 140 return 0; 141 timeout = remaining > INT_MAX ? INT_MAX : (int)remaining; 142 } 143 } 144 145 static int usock_check_connect(int fd) 146 { 147 int err = 0; 148 socklen_t len = sizeof(err); 149 150 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len)) 151 return -1; 152 153 return err ? -1 : 0; 154 } 155 156 static int usock_timeout_remaining(struct timespec *deadline) 157 { 158 struct timespec cur; 159 int64_t msec; 160 161 clock_gettime(CLOCK_MONOTONIC, &cur); 162 msec = ((int64_t)deadline->tv_sec - (int64_t)cur.tv_sec) * 1000; 163 msec += (deadline->tv_nsec - cur.tv_nsec) / 1000000; 164 165 if (msec > INT_MAX) 166 return INT_MAX; 167 168 return msec > 0 ? (int)msec : 0; 169 } 170 171 #define USOCK_MAX_CANDIDATES 8 172 #define USOCK_CONNECT_DELAY_MS 250 173 174 static int usock_addr_interleave(struct addrinfo *result, 175 struct addrinfo **candidates, int max) 176 { 177 struct addrinfo *v6[USOCK_MAX_CANDIDATES], *v4[USOCK_MAX_CANDIDATES]; 178 struct addrinfo *rp; 179 int n_v6 = 0, n_v4 = 0, n = 0, i; 180 181 for (rp = result; rp != NULL; rp = rp->ai_next) { 182 if (rp->ai_family == AF_INET6 && n_v6 < USOCK_MAX_CANDIDATES) 183 v6[n_v6++] = rp; 184 else if (rp->ai_family == AF_INET && n_v4 < USOCK_MAX_CANDIDATES) 185 v4[n_v4++] = rp; 186 } 187 188 for (i = 0; n < max && (i < n_v6 || i < n_v4); i++) { 189 if (i < n_v6 && n < max) 190 candidates[n++] = v6[i]; 191 if (i < n_v4 && n < max) 192 candidates[n++] = v4[i]; 193 } 194 195 return n; 196 } 197 198 int usock_inet_timeout(int type, const char *host, const char *service, 199 void *addr, int timeout) 200 { 201 int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; 202 bool server = !!(type & USOCK_SERVER); 203 struct addrinfo *result, *rp; 204 struct addrinfo hints = { 205 .ai_family = (type & USOCK_IPV6ONLY) ? AF_INET6 : 206 (type & USOCK_IPV4ONLY) ? AF_INET : AF_UNSPEC, 207 .ai_socktype = socktype, 208 .ai_flags = AI_ADDRCONFIG 209 | ((type & USOCK_SERVER) ? AI_PASSIVE : 0) 210 | ((type & USOCK_NUMERIC) ? AI_NUMERICHOST : 0), 211 }; 212 struct addrinfo *candidates[USOCK_MAX_CANDIDATES]; 213 struct addrinfo *pfd_ai[USOCK_MAX_CANDIDATES]; 214 struct pollfd pfds[USOCK_MAX_CANDIDATES]; 215 struct timespec deadline; 216 int n_candidates, n_active = 0; 217 int sock = -1; 218 int fd, delay, i, j; 219 int flags; 220 221 if (getaddrinfo(host, service, &hints, &result)) 222 return -1; 223 224 if (timeout <= 0 || server) { 225 sock = usock_inet_notimeout(type, result, addr); 226 goto free_addrinfo; 227 } 228 229 clock_gettime(CLOCK_MONOTONIC, &deadline); 230 deadline.tv_nsec += (timeout % 1000) * 1000000; 231 if (deadline.tv_nsec >= 1000000000) { 232 deadline.tv_sec++; 233 deadline.tv_nsec -= 1000000000; 234 } 235 deadline.tv_sec += timeout / 1000; 236 237 n_candidates = usock_addr_interleave(result, candidates, 238 USOCK_MAX_CANDIDATES); 239 if (!n_candidates) 240 goto out; 241 242 for (i = 0; i < n_candidates; i++) { 243 rp = candidates[i]; 244 fd = usock_connect(type | USOCK_NONBLOCK, rp->ai_addr, 245 rp->ai_addrlen, rp->ai_family, 246 socktype, server); 247 if (fd < 0) 248 continue; 249 250 pfds[n_active] = (struct pollfd){ .fd = fd, .events = POLLOUT }; 251 pfd_ai[n_active] = rp; 252 n_active++; 253 254 delay = usock_timeout_remaining(&deadline); 255 if (delay <= 0) 256 break; 257 if (i < n_candidates - 1 && delay > USOCK_CONNECT_DELAY_MS) 258 delay = USOCK_CONNECT_DELAY_MS; 259 260 poll_restart(pfds, n_active, delay); 261 262 for (j = n_active - 1; j >= 0; j--) { 263 if (!(pfds[j].revents & POLLOUT)) 264 continue; 265 266 if (usock_check_connect(pfds[j].fd) == 0) { 267 sock = pfds[j].fd; 268 rp = pfd_ai[j]; 269 goto out; 270 } 271 272 close(pfds[j].fd); 273 n_active--; 274 pfds[j] = pfds[n_active]; 275 pfd_ai[j] = pfd_ai[n_active]; 276 } 277 } 278 279 while (n_active > 0) { 280 delay = usock_timeout_remaining(&deadline); 281 if (delay <= 0) 282 break; 283 284 poll_restart(pfds, n_active, delay); 285 286 for (j = n_active - 1; j >= 0; j--) { 287 if (!(pfds[j].revents & POLLOUT)) 288 continue; 289 290 if (usock_check_connect(pfds[j].fd) == 0) { 291 sock = pfds[j].fd; 292 rp = pfd_ai[j]; 293 goto out; 294 } 295 296 close(pfds[j].fd); 297 n_active--; 298 pfds[j] = pfds[n_active]; 299 pfd_ai[j] = pfd_ai[n_active]; 300 } 301 } 302 303 out: 304 for (j = 0; j < n_active; j++) { 305 if (pfds[j].fd != sock) 306 close(pfds[j].fd); 307 } 308 309 if (sock >= 0) { 310 if (!(type & USOCK_NONBLOCK)) { 311 flags = fcntl(sock, F_GETFL); 312 if (flags >= 0) 313 fcntl(sock, F_SETFL, flags & ~O_NONBLOCK); 314 } 315 if (addr) 316 memcpy(addr, rp->ai_addr, rp->ai_addrlen); 317 } 318 free_addrinfo: 319 freeaddrinfo(result); 320 return sock; 321 } 322 323 const char *usock_port(int port) 324 { 325 static char buffer[sizeof("65535\0")]; 326 327 if (port < 0 || port > 65535) 328 return NULL; 329 330 snprintf(buffer, sizeof(buffer), "%u", port); 331 332 return buffer; 333 } 334 335 int usock(int type, const char *host, const char *service) { 336 int sock; 337 338 if (type & USOCK_UNIX) 339 sock = usock_unix(type, host); 340 else 341 sock = usock_inet(type, host, service, NULL); 342 343 if (sock < 0) 344 return -1; 345 346 return sock; 347 } 348 349 int usock_wait_ready(int fd, int msecs) { 350 struct pollfd fds[1]; 351 int res; 352 353 fds[0].fd = fd; 354 fds[0].events = POLLOUT; 355 356 res = poll(fds, 1, msecs); 357 if (res < 0) { 358 return errno; 359 } else if (res == 0) { 360 return -ETIMEDOUT; 361 } else { 362 int err = 0; 363 socklen_t optlen = sizeof(err); 364 365 res = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &optlen); 366 if (res) 367 return errno; 368 if (err) 369 return err; 370 } 371 372 return 0; 373 } 374
This page was automatically generated by LXR 0.3.1. • OpenWrt