]> git.ozlabs.org Git - ccan/blob - ccan/net/net.c
base64: fix for unsigned chars (e.g. ARM).
[ccan] / ccan / net / net.c
1 /* Licensed under BSD-MIT - see LICENSE file for details */
2 #include <ccan/net/net.h>
3 #include <ccan/noerr/noerr.h>
4 #include <poll.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <stdbool.h>
11 #include <netinet/in.h>
12 #include <assert.h>
13
14 struct addrinfo *net_client_lookup(const char *hostname,
15                                    const char *service,
16                                    int family,
17                                    int socktype)
18 {
19         struct addrinfo hints;
20         struct addrinfo *res;
21
22         memset(&hints, 0, sizeof(hints));
23         hints.ai_family = family;
24         hints.ai_socktype = socktype;
25         hints.ai_flags = 0;
26         hints.ai_protocol = 0;
27
28         if (getaddrinfo(hostname, service, &hints, &res) != 0)
29                 return NULL;
30
31         return res;
32 }
33
34 static bool set_nonblock(int fd, bool nonblock)
35 {
36         long flags;
37
38         flags = fcntl(fd, F_GETFL);
39         if (flags == -1)
40                 return false;
41
42         if (nonblock)
43                 flags |= O_NONBLOCK;
44         else
45                 flags &= ~(long)O_NONBLOCK;
46
47         return (fcntl(fd, F_SETFL, flags) == 0);
48 }
49
50 static int start_connect(const struct addrinfo *addr, bool *immediate)
51 {
52         int fd;
53
54         *immediate = false;
55
56         fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
57         if (fd == -1)
58                 return fd;
59
60         if (!set_nonblock(fd, true))
61                 goto close;
62
63         if (connect(fd, addr->ai_addr, addr->ai_addrlen) == 0) {
64                 /* Immediate connect. */
65                 *immediate = true;
66                 return fd;
67         }
68
69         if (errno == EINPROGRESS)
70                 return fd;
71
72 close:
73         close_noerr(fd);
74         return -1;
75 }
76
77
78 int net_connect_async(const struct addrinfo *addrinfo, struct pollfd pfds[2])
79 {
80         const struct addrinfo *addr[2] = { NULL, NULL };
81         unsigned int i;
82
83         pfds[0].fd = pfds[1].fd = -1;
84         pfds[0].events = pfds[1].events = POLLOUT;
85
86         /* Give IPv6 a slight advantage, by trying it first. */
87         for (; addrinfo; addrinfo = addrinfo->ai_next) {
88                 switch (addrinfo->ai_family) {
89                 case AF_INET:
90                         addr[1] = addrinfo;
91                         break;
92                 case AF_INET6:
93                         addr[0] = addrinfo;
94                         break;
95                 default:
96                         continue;
97                 }
98         }
99
100         /* In case we found nothing. */
101         errno = ENOENT;
102         for (i = 0; i < 2; i++) {
103                 bool immediate;
104
105                 if (!addr[i])
106                         continue;
107
108                 pfds[i].fd = start_connect(addr[i], &immediate);
109                 if (immediate) {
110                         if (pfds[!i].fd != -1)
111                                 close(pfds[!i].fd);
112                         if (!set_nonblock(pfds[i].fd, false)) {
113                                 close_noerr(pfds[i].fd);
114                                 return -1;
115                         }
116                         return pfds[i].fd;
117                 }
118         }
119
120         if (pfds[0].fd != -1 || pfds[1].fd != -1)
121                 errno = EINPROGRESS;
122         return -1;
123 }
124
125 void net_connect_abort(struct pollfd pfds[2])
126 {
127         unsigned int i;
128
129         for (i = 0; i < 2; i++) {
130                 if (pfds[i].fd != -1)
131                         close_noerr(pfds[i].fd);
132                 pfds[i].fd = -1;
133         }
134 }
135
136 int net_connect_complete(struct pollfd pfds[2])
137 {
138         unsigned int i;
139
140         assert(pfds[0].fd != -1 || pfds[1].fd != -1);
141
142         for (i = 0; i < 2; i++) {
143                 int err;
144                 socklen_t errlen = sizeof(err);
145
146                 if (pfds[i].fd == -1)
147                         continue;
148                 if (pfds[i].revents & POLLHUP) {
149                         /* Linux gives this if connecting to local
150                          * non-listening port */
151                         close(pfds[i].fd);
152                         pfds[i].fd = -1;
153                         if (pfds[!i].fd == -1) {
154                                 errno = ECONNREFUSED;
155                                 return -1;
156                         }
157                         continue;
158                 }
159                 if (!(pfds[i].revents & POLLOUT))
160                         continue;
161
162                 if (getsockopt(pfds[i].fd, SOL_SOCKET, SO_ERROR, &err,
163                                &errlen) != 0) {
164                         net_connect_abort(pfds);
165                         return -1;
166                 }
167                 if (err == 0) {
168                         /* Don't hand them non-blocking fd! */
169                         if (!set_nonblock(pfds[i].fd, false)) {
170                                 net_connect_abort(pfds);
171                                 return -1;
172                         }
173                         /* Close other one. */
174                         if (pfds[!i].fd != -1)
175                                 close(pfds[!i].fd);
176                         return pfds[i].fd;
177                 }
178         }
179
180         /* Still going... */
181         errno = EINPROGRESS;
182         return -1;
183 }
184
185 int net_connect(const struct addrinfo *addrinfo)
186 {
187         struct pollfd pfds[2];
188         int sockfd;
189
190         sockfd = net_connect_async(addrinfo, pfds);
191         /* Immediate connect or error is easy. */
192         if (sockfd >= 0 || errno != EINPROGRESS)
193                 return sockfd;
194
195         while (poll(pfds, 2, -1) != -1) {
196                 sockfd = net_connect_complete(pfds);
197                 if (sockfd >= 0 || errno != EINPROGRESS)
198                         return sockfd;
199         }
200
201         net_connect_abort(pfds);
202         return -1;
203 }
204
205 struct addrinfo *net_server_lookup(const char *service,
206                                    int family,
207                                    int socktype)
208 {
209         struct addrinfo *res, hints;
210
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;
216
217         if (getaddrinfo(NULL, service, &hints, &res) != 0)
218                 return NULL;
219
220         return res;
221 }
222
223 static bool should_listen(const struct addrinfo *addrinfo)
224 {
225 #ifdef SOCK_SEQPACKET
226         if (addrinfo->ai_socktype == SOCK_SEQPACKET)
227                 return true;
228 #endif
229         return (addrinfo->ai_socktype == SOCK_STREAM);
230 }
231
232 static int make_listen_fd(const struct addrinfo *addrinfo)
233 {
234         int fd, on = 1;
235
236         fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
237                     addrinfo->ai_protocol);
238         if (fd < 0)
239                 return -1;
240
241         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
242                 goto fail;
243
244         if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0)
245                 goto fail;
246
247         if (should_listen(addrinfo) && listen(fd, 5) != 0)
248                 goto fail;
249         return fd;
250
251 fail:
252         close_noerr(fd);
253         return -1;
254 }
255
256 int net_bind(const struct addrinfo *addrinfo, int fds[2])
257 {
258         const struct addrinfo *ipv6 = NULL;
259         const struct addrinfo *ipv4 = NULL;
260         unsigned int num;
261
262         if (addrinfo->ai_family == AF_INET)
263                 ipv4 = addrinfo;
264         else if (addrinfo->ai_family == AF_INET6)
265                 ipv6 = addrinfo;
266
267         if (addrinfo->ai_next) {
268                 if (addrinfo->ai_next->ai_family == AF_INET)
269                         ipv4 = addrinfo->ai_next;
270                 else if (addrinfo->ai_next->ai_family == AF_INET6)
271                         ipv6 = addrinfo->ai_next;
272         }
273
274         num = 0;
275         /* Take IPv6 first, since it might bind to IPv4 port too. */
276         if (ipv6) {
277                 if ((fds[num] = make_listen_fd(ipv6)) >= 0)
278                         num++;
279                 else
280                         ipv6 = NULL;
281         }
282         if (ipv4) {
283                 if ((fds[num] = make_listen_fd(ipv4)) >= 0)
284                         num++;
285                 else
286                         ipv4 = NULL;
287         }
288         if (num == 0)
289                 return -1;
290
291         return num;
292 }