1 /* Licensed under BSD-MIT - see LICENSE file for details */
2 #include <ccan/net/net.h>
3 #include <ccan/noerr/noerr.h>
5 #include <sys/socket.h>
14 #include <netinet/in.h>
17 struct addrinfo *net_client_lookup(const char *hostname,
22 struct addrinfo hints;
25 memset(&hints, 0, sizeof(hints));
26 hints.ai_family = family;
27 hints.ai_socktype = socktype;
29 hints.ai_protocol = 0;
31 if (getaddrinfo(hostname, service, &hints, &res) != 0)
37 static bool set_nonblock(int fd, bool nonblock)
41 flags = fcntl(fd, F_GETFL);
48 flags &= ~(long)O_NONBLOCK;
50 return (fcntl(fd, F_SETFL, flags) == 0);
53 static int start_connect(const struct addrinfo *addr, bool *immediate)
59 fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
63 if (!set_nonblock(fd, true))
66 if (connect(fd, addr->ai_addr, addr->ai_addrlen) == 0) {
67 /* Immediate connect. */
72 if (errno == EINPROGRESS)
81 int net_connect_async(const struct addrinfo *addrinfo, struct pollfd pfds[2])
83 const struct addrinfo *addr[2] = { NULL, NULL };
86 pfds[0].fd = pfds[1].fd = -1;
87 pfds[0].events = pfds[1].events = POLLOUT;
89 /* Give IPv6 a slight advantage, by trying it first. */
90 for (; addrinfo; addrinfo = addrinfo->ai_next) {
91 switch (addrinfo->ai_family) {
103 /* In case we found nothing. */
105 for (i = 0; i < 2; i++) {
111 pfds[i].fd = start_connect(addr[i], &immediate);
113 if (pfds[!i].fd != -1)
115 if (!set_nonblock(pfds[i].fd, false)) {
116 close_noerr(pfds[i].fd);
123 if (pfds[0].fd != -1 || pfds[1].fd != -1)
128 void net_connect_abort(struct pollfd pfds[2])
132 for (i = 0; i < 2; i++) {
133 if (pfds[i].fd != -1)
134 close_noerr(pfds[i].fd);
139 int net_connect_complete(struct pollfd pfds[2])
143 assert(pfds[0].fd != -1 || pfds[1].fd != -1);
145 for (i = 0; i < 2; i++) {
147 socklen_t errlen = sizeof(err);
149 if (pfds[i].fd == -1)
151 if (getsockopt(pfds[i].fd, SOL_SOCKET, SO_ERROR, &err,
153 net_connect_abort(pfds);
157 /* Don't hand them non-blocking fd! */
158 if (!set_nonblock(pfds[i].fd, false)) {
159 net_connect_abort(pfds);
162 /* Close other one. */
163 if (pfds[!i].fd != -1)
174 int net_connect(const struct addrinfo *addrinfo)
176 struct pollfd pfds[2];
179 sockfd = net_connect_async(addrinfo, pfds);
180 /* Immediate connect or error is easy. */
181 if (sockfd >= 0 || errno != EINPROGRESS)
184 while (poll(pfds, 2, -1) != -1) {
185 sockfd = net_connect_complete(pfds);
186 if (sockfd >= 0 || errno != EINPROGRESS)
190 net_connect_abort(pfds);
194 struct addrinfo *net_server_lookup(const char *service,
198 struct addrinfo *res, hints;
200 memset(&hints, 0, sizeof(hints));
201 hints.ai_family = family;
202 hints.ai_socktype = socktype;
203 hints.ai_flags = AI_PASSIVE;
204 hints.ai_protocol = 0;
206 if (getaddrinfo(NULL, service, &hints, &res) != 0)
212 static bool should_listen(const struct addrinfo *addrinfo)
214 #ifdef SOCK_SEQPACKET
215 if (addrinfo->ai_socktype == SOCK_SEQPACKET)
218 return (addrinfo->ai_socktype == SOCK_STREAM);
221 static int make_listen_fd(const struct addrinfo *addrinfo)
223 int saved_errno, fd, on = 1;
225 fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
226 addrinfo->ai_protocol);
230 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
231 if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0)
234 if (should_listen(addrinfo) && listen(fd, 5) != 0)
245 int net_bind(const struct addrinfo *addrinfo, int fds[2])
247 const struct addrinfo *ipv6 = NULL;
248 const struct addrinfo *ipv4 = NULL;
251 if (addrinfo->ai_family == AF_INET)
253 else if (addrinfo->ai_family == AF_INET6)
256 if (addrinfo->ai_next) {
257 if (addrinfo->ai_next->ai_family == AF_INET)
258 ipv4 = addrinfo->ai_next;
259 else if (addrinfo->ai_next->ai_family == AF_INET6)
260 ipv6 = addrinfo->ai_next;
264 /* Take IPv6 first, since it might bind to IPv4 port too. */
266 if ((fds[num] = make_listen_fd(ipv6)) >= 0)
272 if ((fds[num] = make_listen_fd(ipv4)) >= 0)