1 /* Licensed under BSD-MIT - see LICENSE file for details */
2 #include <ccan/net/net.h>
4 #include <sys/socket.h>
13 #include <netinet/in.h>
15 struct addrinfo *net_client_lookup(const char *hostname,
20 struct addrinfo hints;
23 memset(&hints, 0, sizeof(hints));
24 hints.ai_family = family;
25 hints.ai_socktype = socktype;
27 hints.ai_protocol = 0;
29 if (getaddrinfo(hostname, service, &hints, &res) != 0)
35 static bool set_nonblock(int fd, bool nonblock)
39 flags = fcntl(fd, F_GETFL);
46 flags &= ~(long)O_NONBLOCK;
48 return (fcntl(fd, F_SETFL, flags) == 0);
51 /* We only handle IPv4 and IPv6 */
54 static void remove_fd(struct pollfd pfd[],
55 const struct addrinfo *addr[],
60 memmove(pfd + i, pfd + i + 1, (*num - i - 1) * sizeof(pfd[0]));
61 memmove(addr + i, addr + i + 1, (*num - i - 1) * sizeof(addr[0]));
62 memmove(slen + i, slen + i + 1, (*num - i - 1) * sizeof(slen[0]));
66 int net_connect(const struct addrinfo *addrinfo)
68 int sockfd = -1, saved_errno;
70 const struct addrinfo *ipv4 = NULL, *ipv6 = NULL;
71 const struct addrinfo *addr[MAX_PROTOS];
72 socklen_t slen[MAX_PROTOS];
73 struct pollfd pfd[MAX_PROTOS];
75 for (; addrinfo; addrinfo = addrinfo->ai_next) {
76 switch (addrinfo->ai_family) {
89 /* We give IPv6 a slight edge by connecting it first. */
92 slen[num] = sizeof(struct sockaddr_in6);
93 pfd[num].fd = socket(AF_INET6, ipv6->ai_socktype,
95 if (pfd[num].fd != -1)
100 slen[num] = sizeof(struct sockaddr_in);
101 pfd[num].fd = socket(AF_INET, ipv4->ai_socktype,
103 if (pfd[num].fd != -1)
107 for (i = 0; i < num; i++) {
108 if (!set_nonblock(pfd[i].fd, true)) {
109 remove_fd(pfd, addr, slen, &num, i--);
112 /* Connect *can* be instant. */
113 if (connect(pfd[i].fd, addr[i]->ai_addr, slen[i]) == 0)
115 if (errno != EINPROGRESS) {
116 /* Remove dead one. */
117 remove_fd(pfd, addr, slen, &num, i--);
119 pfd[i].events = POLLOUT;
122 while (num && poll(pfd, num, -1) != -1) {
123 for (i = 0; i < num; i++) {
125 socklen_t errlen = sizeof(err);
128 if (getsockopt(pfd[i].fd, SOL_SOCKET, SO_ERROR, &err,
134 /* Remove dead one. */
136 remove_fd(pfd, addr, slen, &num, i--);
141 /* We don't want to hand them a non-blocking socket! */
142 if (set_nonblock(pfd[i].fd, false))
147 for (i = 0; i < num; i++)
148 if (pfd[i].fd != sockfd)
154 struct addrinfo *net_server_lookup(const char *service,
158 struct addrinfo *res, hints;
160 memset(&hints, 0, sizeof(hints));
161 hints.ai_family = family;
162 hints.ai_socktype = socktype;
163 hints.ai_flags = AI_PASSIVE;
164 hints.ai_protocol = 0;
166 if (getaddrinfo(NULL, service, &hints, &res) != 0)
172 static bool should_listen(const struct addrinfo *addrinfo)
174 #ifdef SOCK_SEQPACKET
175 if (addrinfo->ai_socktype == SOCK_SEQPACKET)
178 return (addrinfo->ai_socktype == SOCK_STREAM);
181 static int make_listen_fd(const struct addrinfo *addrinfo)
183 int saved_errno, fd, on = 1;
185 fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
186 addrinfo->ai_protocol);
190 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
191 if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0)
194 if (should_listen(addrinfo) && listen(fd, 5) != 0)
205 int net_bind(const struct addrinfo *addrinfo, int fds[2])
207 const struct addrinfo *ipv6 = NULL;
208 const struct addrinfo *ipv4 = NULL;
211 if (addrinfo->ai_family == AF_INET)
213 else if (addrinfo->ai_family == AF_INET6)
216 if (addrinfo->ai_next) {
217 if (addrinfo->ai_next->ai_family == AF_INET)
218 ipv4 = addrinfo->ai_next;
219 else if (addrinfo->ai_next->ai_family == AF_INET6)
220 ipv6 = addrinfo->ai_next;
224 /* Take IPv6 first, since it might bind to IPv4 port too. */
226 if ((fds[num] = make_listen_fd(ipv6)) >= 0)
232 if ((fds[num] = make_listen_fd(ipv4)) >= 0)