net: remove noerr dependency.
[ccan] / ccan / net / net.c
1 #include <ccan/net/net.h>
2 #include <sys/types.h>
3 #include <sys/socket.h>
4 #include <poll.h>
5 #include <netdb.h>
6 #include <string.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <errno.h>
11 #include <stdbool.h>
12
13 struct addrinfo *net_client_lookup(const char *hostname,
14                                    const char *service,
15                                    int family,
16                                    int socktype)
17 {
18         struct addrinfo hints;
19         struct addrinfo *res;
20
21         memset(&hints, 0, sizeof(hints));
22         hints.ai_family = family;
23         hints.ai_socktype = socktype;
24         hints.ai_flags = 0;
25         hints.ai_protocol = 0;
26
27         if (getaddrinfo(hostname, service, &hints, &res) != 0)
28                 return NULL;
29
30         return res;
31 }
32
33 static bool set_nonblock(int fd, bool nonblock)
34 {
35         long flags;
36
37         flags = fcntl(fd, F_GETFL);
38         if (flags == -1)
39                 return false;
40
41         if (nonblock)
42                 flags |= O_NONBLOCK;
43         else
44                 flags &= ~(long)O_NONBLOCK;
45
46         return (fcntl(fd, F_SETFL, flags) == 0);
47 }
48
49 /* We only handle IPv4 and IPv6 */
50 #define MAX_PROTOS 2
51
52 static void remove_fd(struct pollfd pfd[],
53                       const struct addrinfo *addr[],
54                       socklen_t slen[],
55                       unsigned int *num,
56                       unsigned int i)
57 {
58         memmove(pfd + i, pfd + i + 1, (*num - i - 1) * sizeof(pfd[0]));
59         memmove(addr + i, addr + i + 1, (*num - i - 1) * sizeof(addr[0]));
60         memmove(slen + i, slen + i + 1, (*num - i - 1) * sizeof(slen[0]));
61         (*num)--;
62 }
63
64 int net_connect(const struct addrinfo *addrinfo)
65 {
66         int sockfd = -1, saved_errno;
67         unsigned int i, num;
68         const struct addrinfo *ipv4 = NULL, *ipv6 = NULL;
69         const struct addrinfo *addr[MAX_PROTOS];
70         socklen_t slen[MAX_PROTOS];
71         struct pollfd pfd[MAX_PROTOS];
72
73         for (; addrinfo; addrinfo = addrinfo->ai_next) {
74                 switch (addrinfo->ai_family) {
75                 case AF_INET:
76                         if (!ipv4)
77                                 ipv4 = addrinfo;
78                         break;
79                 case AF_INET6:
80                         if (!ipv6)
81                                 ipv6 = addrinfo;
82                         break;
83                 }
84         }
85
86         num = 0;
87         /* We give IPv6 a slight edge by connecting it first. */
88         if (ipv6) {
89                 addr[num] = ipv6;
90                 slen[num] = sizeof(struct sockaddr_in6);
91                 pfd[num].fd = socket(AF_INET6, ipv6->ai_socktype,
92                                      ipv6->ai_protocol);
93                 if (pfd[num].fd != -1)
94                         num++;
95         }
96         if (ipv4) {
97                 addr[num] = ipv4;
98                 slen[num] = sizeof(struct sockaddr_in);
99                 pfd[num].fd = socket(AF_INET, ipv4->ai_socktype,
100                                      ipv4->ai_protocol);
101                 if (pfd[num].fd != -1)
102                         num++;
103         }
104
105         for (i = 0; i < num; i++) {
106                 if (!set_nonblock(pfd[i].fd, true)) {
107                         remove_fd(pfd, addr, slen, &num, i--);
108                         continue;
109                 }
110                 /* Connect *can* be instant. */
111                 if (connect(pfd[i].fd, addr[i]->ai_addr, slen[i]) == 0)
112                         goto got_one;
113                 if (errno != EINPROGRESS) {
114                         /* Remove dead one. */
115                         remove_fd(pfd, addr, slen, &num, i--);
116                 }
117                 pfd[i].events = POLLOUT;
118         }
119
120         while (num && poll(pfd, num, -1) != -1) {
121                 for (i = 0; i < num; i++) {
122                         int err;
123                         socklen_t errlen = sizeof(err);
124                         if (!pfd[i].revents)
125                                 continue;
126                         if (getsockopt(pfd[i].fd, SOL_SOCKET, SO_ERROR, &err,
127                                        &errlen) != 0)
128                                 goto out;
129                         if (err == 0)
130                                 goto got_one;
131
132                         /* Remove dead one. */
133                         errno = err;
134                         remove_fd(pfd, addr, slen, &num, i--);
135                 }
136         }
137
138 got_one:
139         /* We don't want to hand them a non-blocking socket! */
140         if (set_nonblock(pfd[i].fd, false))
141                 sockfd = pfd[i].fd;
142
143 out:
144         saved_errno = errno;
145         for (i = 0; i < num; i++)
146                 if (pfd[i].fd != sockfd)
147                         close(pfd[i].fd);
148         errno = saved_errno;
149         return sockfd;
150 }