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 <stdio.h> 31 #include <poll.h> 32 33 #include "usock.h" 34 #include "utils.h" 35 36 static void usock_set_flags(int sock, unsigned int type) 37 { 38 if (!(type & USOCK_NOCLOEXEC)) 39 fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC); 40 41 if (type & USOCK_NONBLOCK) 42 fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK); 43 } 44 45 static int usock_connect(int type, struct sockaddr *sa, int sa_len, int family, int socktype, bool server) 46 { 47 int sock; 48 49 sock = socket(family, socktype, 0); 50 if (sock < 0) 51 return -1; 52 53 usock_set_flags(sock, type); 54 55 if (server) { 56 const int one = 1; 57 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); 58 59 if (!bind(sock, sa, sa_len) && 60 (socktype != SOCK_STREAM || !listen(sock, SOMAXCONN))) 61 return sock; 62 } else { 63 if (!connect(sock, sa, sa_len) || errno == EINPROGRESS) 64 return sock; 65 } 66 67 close(sock); 68 return -1; 69 } 70 71 static int usock_unix(int type, const char *host) 72 { 73 struct sockaddr_un sun = {.sun_family = AF_UNIX}; 74 bool server = !!(type & USOCK_SERVER); 75 int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; 76 77 if (strlen(host) >= sizeof(sun.sun_path)) { 78 errno = EINVAL; 79 return -1; 80 } 81 strcpy(sun.sun_path, host); 82 83 return usock_connect(type, (struct sockaddr*)&sun, sizeof(sun), AF_UNIX, socktype, server); 84 } 85 86 static int 87 usock_inet_notimeout(int type, struct addrinfo *result, void *addr) 88 { 89 struct addrinfo *rp; 90 int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; 91 bool server = !!(type & USOCK_SERVER); 92 int sock; 93 94 for (rp = result; rp != NULL; rp = rp->ai_next) { 95 sock = usock_connect(type, rp->ai_addr, rp->ai_addrlen, rp->ai_family, socktype, server); 96 if (sock >= 0) { 97 if (addr) 98 memcpy(addr, rp->ai_addr, rp->ai_addrlen); 99 return sock; 100 } 101 } 102 103 return -1; 104 } 105 106 static int poll_restart(struct pollfd *fds, int nfds, int timeout) 107 { 108 struct timespec ts, cur; 109 int msec = timeout % 1000; 110 int ret; 111 112 clock_gettime(CLOCK_MONOTONIC, &ts); 113 114 ts.tv_nsec += msec * 1000000; 115 if (ts.tv_nsec > 1000000000) { 116 ts.tv_sec++; 117 ts.tv_nsec -= 1000000000; 118 } 119 ts.tv_sec += timeout / 1000; 120 121 while (1) { 122 ret = poll(fds, nfds, timeout); 123 if (ret >= 0 || (errno != EINTR && errno != EAGAIN)) 124 return ret; 125 126 clock_gettime(CLOCK_MONOTONIC, &cur); 127 timeout = (ts.tv_sec - cur.tv_sec) * 1000; 128 timeout += (ts.tv_nsec - cur.tv_nsec) / 1000000; 129 if (timeout <= 0) 130 return 0; 131 } 132 } 133 134 int usock_inet_timeout(int type, const char *host, const char *service, 135 void *addr, int timeout) 136 { 137 int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; 138 bool server = !!(type & USOCK_SERVER); 139 struct addrinfo *result, *rp; 140 struct addrinfo hints = { 141 .ai_family = (type & USOCK_IPV6ONLY) ? AF_INET6 : 142 (type & USOCK_IPV4ONLY) ? AF_INET : AF_UNSPEC, 143 .ai_socktype = socktype, 144 .ai_flags = AI_ADDRCONFIG 145 | ((type & USOCK_SERVER) ? AI_PASSIVE : 0) 146 | ((type & USOCK_NUMERIC) ? AI_NUMERICHOST : 0), 147 }; 148 struct addrinfo *rp_v6 = NULL; 149 struct addrinfo *rp_v4 = NULL; 150 struct pollfd pfds[2] = { 151 { .fd = -1, .events = POLLOUT }, 152 { .fd = -1, .events = POLLOUT }, 153 }; 154 int sock = -1; 155 int i; 156 157 if (getaddrinfo(host, service, &hints, &result)) 158 return -1; 159 160 if (timeout <= 0 || server) { 161 sock = usock_inet_notimeout(type, result, addr); 162 goto free_addrinfo; 163 } 164 165 for (rp = result; rp != NULL; rp = rp->ai_next) { 166 if (rp->ai_family == AF_INET6 && !rp_v6) 167 rp_v6 = rp; 168 if (rp->ai_family == AF_INET && !rp_v4) 169 rp_v4 = rp; 170 } 171 172 if (!rp_v6 && !rp_v4) 173 goto out; 174 175 if (rp_v6) { 176 rp = rp_v6; 177 pfds[0].fd = usock_connect(type | USOCK_NONBLOCK, rp->ai_addr, 178 rp->ai_addrlen, rp->ai_family, 179 socktype, server); 180 if (pfds[0].fd < 0) { 181 rp_v6 = NULL; 182 goto try_v4; 183 } 184 185 if (timeout > 300) { 186 if (poll_restart(pfds, 1, 300) == 1) { 187 rp = rp_v6; 188 sock = pfds[0].fd; 189 goto out; 190 } 191 } 192 timeout -= 300; 193 } 194 195 try_v4: 196 if (rp_v4) { 197 rp = rp_v4; 198 pfds[1].fd = usock_connect(type | USOCK_NONBLOCK, rp->ai_addr, 199 rp->ai_addrlen, rp->ai_family, 200 socktype, server); 201 if (pfds[1].fd < 0) { 202 rp_v4 = NULL; 203 if (!rp_v6) 204 goto out; 205 goto wait; 206 } 207 } 208 209 wait: 210 poll_restart(pfds + !rp_v6, !!rp_v6 + !!rp_v4, timeout); 211 if (pfds[0].revents & POLLOUT) { 212 rp = rp_v6; 213 sock = pfds[0].fd; 214 goto out; 215 } 216 217 if (pfds[1].revents & POLLOUT) { 218 rp = rp_v4; 219 sock = pfds[1].fd; 220 goto out; 221 } 222 223 out: 224 for (i = 0; i < 2; i++) { 225 int fd = pfds[i].fd; 226 if (fd >= 0 && fd != sock) 227 close(fd); 228 } 229 230 if (!(type & USOCK_NONBLOCK)) 231 fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) & ~O_NONBLOCK); 232 233 if (addr && sock >= 0) 234 memcpy(addr, rp->ai_addr, rp->ai_addrlen); 235 free_addrinfo: 236 freeaddrinfo(result); 237 return sock; 238 } 239 240 const char *usock_port(int port) 241 { 242 static char buffer[sizeof("65535\0")]; 243 244 if (port < 0 || port > 65535) 245 return NULL; 246 247 snprintf(buffer, sizeof(buffer), "%u", port); 248 249 return buffer; 250 } 251 252 int usock(int type, const char *host, const char *service) { 253 int sock; 254 255 if (type & USOCK_UNIX) 256 sock = usock_unix(type, host); 257 else 258 sock = usock_inet(type, host, service, NULL); 259 260 if (sock < 0) 261 return -1; 262 263 return sock; 264 } 265 266 int usock_wait_ready(int fd, int msecs) { 267 struct pollfd fds[1]; 268 int res; 269 270 fds[0].fd = fd; 271 fds[0].events = POLLOUT; 272 273 res = poll(fds, 1, msecs); 274 if (res < 0) { 275 return errno; 276 } else if (res == 0) { 277 return -ETIMEDOUT; 278 } else { 279 int err = 0; 280 socklen_t optlen = sizeof(err); 281 282 res = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &optlen); 283 if (res) 284 return errno; 285 if (err) 286 return err; 287 } 288 289 return 0; 290 } 291
This page was automatically generated by LXR 0.3.1. • OpenWrt