1 /* Licensed under BSD-MIT - see LICENSE file for details */
2 #include <ccan/net/net.h>
3 #include <ccan/noerr/noerr.h>
11 #include <netinet/in.h>
14 struct addrinfo *net_client_lookup(const char *hostname,
19 struct addrinfo hints;
22 memset(&hints, 0, sizeof(hints));
23 hints.ai_family = family;
24 hints.ai_socktype = socktype;
26 hints.ai_protocol = 0;
28 if (getaddrinfo(hostname, service, &hints, &res) != 0)
34 static bool set_nonblock(int fd, bool nonblock)
38 flags = fcntl(fd, F_GETFL);
45 flags &= ~(long)O_NONBLOCK;
47 return (fcntl(fd, F_SETFL, flags) == 0);
50 static int start_connect(const struct addrinfo *addr, bool *immediate)
56 fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
60 if (!set_nonblock(fd, true))
63 if (connect(fd, addr->ai_addr, addr->ai_addrlen) == 0) {
64 /* Immediate connect. */
69 if (errno == EINPROGRESS)
78 int net_connect_async(const struct addrinfo *addrinfo, struct pollfd pfds[2])
80 const struct addrinfo *addr[2] = { NULL, NULL };
83 pfds[0].fd = pfds[1].fd = -1;
84 pfds[0].events = pfds[1].events = POLLOUT;
86 /* Give IPv6 a slight advantage, by trying it first. */
87 for (; addrinfo; addrinfo = addrinfo->ai_next) {
88 switch (addrinfo->ai_family) {
100 /* In case we found nothing. */
102 for (i = 0; i < 2; i++) {
108 pfds[i].fd = start_connect(addr[i], &immediate);
110 if (pfds[!i].fd != -1)
112 if (!set_nonblock(pfds[i].fd, false)) {
113 close_noerr(pfds[i].fd);
120 if (pfds[0].fd != -1 || pfds[1].fd != -1)
125 void net_connect_abort(struct pollfd pfds[2])
129 for (i = 0; i < 2; i++) {
130 if (pfds[i].fd != -1)
131 close_noerr(pfds[i].fd);
136 int net_connect_complete(struct pollfd pfds[2])
140 assert(pfds[0].fd != -1 || pfds[1].fd != -1);
142 for (i = 0; i < 2; i++) {
144 socklen_t errlen = sizeof(err);
146 if (pfds[i].fd == -1)
148 if (pfds[i].revents & POLLHUP) {
149 /* Linux gives this if connecting to local
150 * non-listening port */
153 if (pfds[!i].fd == -1) {
154 errno = ECONNREFUSED;
159 if (!(pfds[i].revents & POLLOUT))
162 if (getsockopt(pfds[i].fd, SOL_SOCKET, SO_ERROR, &err,
164 net_connect_abort(pfds);
168 /* Don't hand them non-blocking fd! */
169 if (!set_nonblock(pfds[i].fd, false)) {
170 net_connect_abort(pfds);
173 /* Close other one. */
174 if (pfds[!i].fd != -1)
185 int net_connect(const struct addrinfo *addrinfo)
187 struct pollfd pfds[2];
190 sockfd = net_connect_async(addrinfo, pfds);
191 /* Immediate connect or error is easy. */
192 if (sockfd >= 0 || errno != EINPROGRESS)
195 while (poll(pfds, 2, -1) != -1) {
196 sockfd = net_connect_complete(pfds);
197 if (sockfd >= 0 || errno != EINPROGRESS)
201 net_connect_abort(pfds);
205 struct addrinfo *net_server_lookup(const char *service,
209 struct addrinfo *res, hints;
211 memset(&hints, 0, sizeof(hints));
212 hints.ai_family = family;
213 hints.ai_socktype = socktype;
214 hints.ai_flags = AI_PASSIVE;
215 hints.ai_protocol = 0;
217 if (getaddrinfo(NULL, service, &hints, &res) != 0)
223 static bool should_listen(const struct addrinfo *addrinfo)
225 #ifdef SOCK_SEQPACKET
226 if (addrinfo->ai_socktype == SOCK_SEQPACKET)
229 return (addrinfo->ai_socktype == SOCK_STREAM);
232 static int make_listen_fd(const struct addrinfo *addrinfo)
236 fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
237 addrinfo->ai_protocol);
241 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
242 if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0)
245 if (should_listen(addrinfo) && listen(fd, 5) != 0)
254 int net_bind(const struct addrinfo *addrinfo, int fds[2])
256 const struct addrinfo *ipv6 = NULL;
257 const struct addrinfo *ipv4 = NULL;
260 if (addrinfo->ai_family == AF_INET)
262 else if (addrinfo->ai_family == AF_INET6)
265 if (addrinfo->ai_next) {
266 if (addrinfo->ai_next->ai_family == AF_INET)
267 ipv4 = addrinfo->ai_next;
268 else if (addrinfo->ai_next->ai_family == AF_INET6)
269 ipv6 = addrinfo->ai_next;
273 /* Take IPv6 first, since it might bind to IPv4 port too. */
275 if ((fds[num] = make_listen_fd(ipv6)) >= 0)
281 if ((fds[num] = make_listen_fd(ipv4)) >= 0)