• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/libubox/usock.c

  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