2 * usock - socket helper functions
4 * Copyright (C) 2010 Steven Barth <steven@midlink.org>
5 * Copyright (C) 2011-2012 Felix Fietkau <nbd@openwrt.org>
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.
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.
19 #include <sys/types.h>
20 #include <sys/socket.h>
36 static void usock_set_flags(int sock
, unsigned int type
)
38 if (!(type
& USOCK_NOCLOEXEC
))
39 fcntl(sock
, F_SETFD
, fcntl(sock
, F_GETFD
) | FD_CLOEXEC
);
41 if (type
& USOCK_NONBLOCK
)
42 fcntl(sock
, F_SETFL
, fcntl(sock
, F_GETFL
) | O_NONBLOCK
);
45 static int usock_connect(int type
, struct sockaddr
*sa
, int sa_len
, int family
, int socktype
, bool server
)
49 sock
= socket(family
, socktype
, 0);
53 usock_set_flags(sock
, type
);
57 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
));
59 if (!bind(sock
, sa
, sa_len
) &&
60 (socktype
!= SOCK_STREAM
|| !listen(sock
, SOMAXCONN
)))
63 if (!connect(sock
, sa
, sa_len
) || errno
== EINPROGRESS
)
71 static int usock_unix(int type
, const char *host
)
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
;
77 if (strlen(host
) >= sizeof(sun
.sun_path
)) {
81 strcpy(sun
.sun_path
, host
);
83 return usock_connect(type
, (struct sockaddr
*)&sun
, sizeof(sun
), AF_UNIX
, socktype
, server
);
87 usock_inet_notimeout(int type
, struct addrinfo
*result
, void *addr
)
90 int socktype
= ((type
& 0xff) == USOCK_TCP
) ? SOCK_STREAM
: SOCK_DGRAM
;
91 bool server
= !!(type
& USOCK_SERVER
);
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
);
98 memcpy(addr
, rp
->ai_addr
, rp
->ai_addrlen
);
106 static int poll_restart(struct pollfd
*fds
, int nfds
, int timeout
)
108 struct timespec ts
, cur
;
109 int msec
= timeout
% 1000;
112 clock_gettime(CLOCK_MONOTONIC
, &ts
);
114 ts
.tv_nsec
+= msec
* 1000000;
115 if (ts
.tv_nsec
> 1000000000) {
117 ts
.tv_nsec
-= 1000000000;
119 ts
.tv_sec
+= timeout
/ 1000;
122 ret
= poll(fds
, nfds
, timeout
);
123 if (ret
>= 0 || (errno
!= EINTR
&& errno
!= EAGAIN
))
126 clock_gettime(CLOCK_MONOTONIC
, &cur
);
127 timeout
= (ts
.tv_sec
- cur
.tv_sec
) * 1000;
128 timeout
+= (ts
.tv_nsec
- cur
.tv_nsec
) / 1000000;
134 int usock_inet_timeout(int type
, const char *host
, const char *service
,
135 void *addr
, int timeout
)
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),
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
},
157 if (getaddrinfo(host
, service
, &hints
, &result
))
160 if (timeout
<= 0 || server
) {
161 sock
= usock_inet_notimeout(type
, result
, addr
);
165 for (rp
= result
; rp
!= NULL
; rp
= rp
->ai_next
) {
166 if (rp
->ai_family
== AF_INET6
&& !rp_v6
)
168 if (rp
->ai_family
== AF_INET
&& !rp_v4
)
172 if (!rp_v6
&& !rp_v4
)
177 pfds
[0].fd
= usock_connect(type
| USOCK_NONBLOCK
, rp
->ai_addr
,
178 rp
->ai_addrlen
, rp
->ai_family
,
180 if (pfds
[0].fd
< 0) {
186 if (poll_restart(pfds
, 1, 300) == 1) {
198 pfds
[1].fd
= usock_connect(type
| USOCK_NONBLOCK
, rp
->ai_addr
,
199 rp
->ai_addrlen
, rp
->ai_family
,
201 if (pfds
[1].fd
< 0) {
210 poll_restart(pfds
+ !rp_v6
, !!rp_v6
+ !!rp_v4
, timeout
);
211 if (pfds
[0].revents
& POLLOUT
) {
217 if (pfds
[1].revents
& POLLOUT
) {
224 for (i
= 0; i
< 2; i
++) {
226 if (fd
>= 0 && fd
!= sock
)
230 if (!(type
& USOCK_NONBLOCK
))
231 fcntl(sock
, F_SETFL
, fcntl(sock
, F_GETFL
) & ~O_NONBLOCK
);
233 if (addr
&& sock
>= 0)
234 memcpy(addr
, rp
->ai_addr
, rp
->ai_addrlen
);
236 freeaddrinfo(result
);
240 const char *usock_port(int port
)
242 static char buffer
[sizeof("65535\0")];
244 if (port
< 0 || port
> 65535)
247 snprintf(buffer
, sizeof(buffer
), "%u", port
);
252 int usock(int type
, const char *host
, const char *service
) {
255 if (type
& USOCK_UNIX
)
256 sock
= usock_unix(type
, host
);
258 sock
= usock_inet(type
, host
, service
, NULL
);
266 int usock_wait_ready(int fd
, int msecs
) {
267 struct pollfd fds
[1];
271 fds
[0].events
= POLLOUT
;
273 res
= poll(fds
, 1, msecs
);
276 } else if (res
== 0) {
280 socklen_t optlen
= sizeof(err
);
282 res
= getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &err
, &optlen
);