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 (pfds[i].revents & POLLHUP) {
152 /* Linux gives this if connecting to local
153 * non-listening port */
156 if (pfds[!i].fd == -1) {
157 errno = ECONNREFUSED;
162 if (!(pfds[i].revents & POLLOUT))
165 if (getsockopt(pfds[i].fd, SOL_SOCKET, SO_ERROR, &err,
167 net_connect_abort(pfds);
171 /* Don't hand them non-blocking fd! */
172 if (!set_nonblock(pfds[i].fd, false)) {
173 net_connect_abort(pfds);
176 /* Close other one. */
177 if (pfds[!i].fd != -1)
188 int net_connect(const struct addrinfo *addrinfo)
190 struct pollfd pfds[2];
193 sockfd = net_connect_async(addrinfo, pfds);
194 /* Immediate connect or error is easy. */
195 if (sockfd >= 0 || errno != EINPROGRESS)
198 while (poll(pfds, 2, -1) != -1) {
199 sockfd = net_connect_complete(pfds);
200 if (sockfd >= 0 || errno != EINPROGRESS)
204 net_connect_abort(pfds);
208 struct addrinfo *net_server_lookup(const char *service,
212 struct addrinfo *res, hints;
214 memset(&hints, 0, sizeof(hints));
215 hints.ai_family = family;
216 hints.ai_socktype = socktype;
217 hints.ai_flags = AI_PASSIVE;
218 hints.ai_protocol = 0;
220 if (getaddrinfo(NULL, service, &hints, &res) != 0)
226 static bool should_listen(const struct addrinfo *addrinfo)
228 #ifdef SOCK_SEQPACKET
229 if (addrinfo->ai_socktype == SOCK_SEQPACKET)
232 return (addrinfo->ai_socktype == SOCK_STREAM);
235 static int make_listen_fd(const struct addrinfo *addrinfo)
237 int saved_errno, fd, on = 1;
239 fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
240 addrinfo->ai_protocol);
244 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
245 if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0)
248 if (should_listen(addrinfo) && listen(fd, 5) != 0)
259 int net_bind(const struct addrinfo *addrinfo, int fds[2])
261 const struct addrinfo *ipv6 = NULL;
262 const struct addrinfo *ipv4 = NULL;
265 if (addrinfo->ai_family == AF_INET)
267 else if (addrinfo->ai_family == AF_INET6)
270 if (addrinfo->ai_next) {
271 if (addrinfo->ai_next->ai_family == AF_INET)
272 ipv4 = addrinfo->ai_next;
273 else if (addrinfo->ai_next->ai_family == AF_INET6)
274 ipv6 = addrinfo->ai_next;
278 /* Take IPv6 first, since it might bind to IPv4 port too. */
280 if ((fds[num] = make_listen_fd(ipv6)) >= 0)
286 if ((fds[num] = make_listen_fd(ipv4)) >= 0)