chiark / gitweb /
use linux/sched.h instead of sched.h for older glibc
[elogind.git] / socket-util.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <assert.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <errno.h>
7 #include <stdlib.h>
8 #include <arpa/inet.h>
9 #include <stdio.h>
10 #include <net/if.h>
11
12 #include "macro.h"
13 #include "util.h"
14 #include "socket-util.h"
15
16 int socket_address_parse(SocketAddress *a, const char *s) {
17         int r;
18         char *e, *n;
19         unsigned u;
20
21         assert(a);
22         assert(s);
23
24         zero(*a);
25         a->type = SOCK_STREAM;
26
27         if (*s == '[') {
28                 /* IPv6 in [x:.....:z]:p notation */
29
30                 if (!(e = strchr(s+1, ']')))
31                         return -EINVAL;
32
33                 if (!(n = strndup(s+1, e-s-1)))
34                         return -ENOMEM;
35
36                 errno = 0;
37                 if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) {
38                         free(n);
39                         return errno != 0 ? -errno : -EINVAL;
40                 }
41
42                 free(n);
43
44                 e++;
45                 if (*e != ':')
46                         return -EINVAL;
47
48                 e++;
49                 if ((r = safe_atou(e, &u)) < 0)
50                         return r;
51
52                 if (u <= 0 || u > 0xFFFF)
53                         return -EINVAL;
54
55                 a->sockaddr.in6.sin6_family = AF_INET6;
56                 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
57                 a->size = sizeof(struct sockaddr_in6);
58
59         } else if (*s == '/') {
60                 /* AF_UNIX socket */
61
62                 size_t l;
63
64                 l = strlen(s);
65                 if (l >= sizeof(a->sockaddr.un.sun_path))
66                         return -EINVAL;
67
68                 a->sockaddr.un.sun_family = AF_UNIX;
69                 memcpy(a->sockaddr.un.sun_path, s, l);
70                 a->size = sizeof(sa_family_t) + l + 1;
71
72         } else if (*s == '@') {
73                 /* Abstract AF_UNIX socket */
74                 size_t l;
75
76                 l = strlen(s+1);
77                 if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
78                         return -EINVAL;
79
80                 a->sockaddr.un.sun_family = AF_UNIX;
81                 memcpy(a->sockaddr.un.sun_path+1, s+1, l);
82                 a->size = sizeof(struct sockaddr_un);
83
84         } else {
85
86                 if ((e = strchr(s, ':'))) {
87                         int r;
88
89                         if ((r = safe_atou(e+1, &u)) < 0)
90                                 return r;
91
92                         if (u <= 0 || u > 0xFFFF)
93                                 return -EINVAL;
94
95                         if (!(n = strndup(s, e-s)))
96                                 return -ENOMEM;
97
98                         /* IPv4 in w.x.y.z:p notation? */
99                         if ((r = inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr)) < 0) {
100                                 free(n);
101                                 return -errno;
102                         }
103
104                         if (r > 0) {
105                                 /* Gotcha, it's a traditional IPv4 address */
106                                 free(n);
107
108                                 a->sockaddr.in4.sin_family = AF_INET;
109                                 a->sockaddr.in4.sin_port = htons((uint16_t) u);
110                                 a->size = sizeof(struct sockaddr_in);
111                         } else {
112                                 unsigned idx;
113
114                                 if (strlen(n) > IF_NAMESIZE-1) {
115                                         free(n);
116                                         return -EINVAL;
117                                 }
118
119                                 /* Uh, our last resort, an interface name */
120                                 idx = if_nametoindex(n);
121                                 free(n);
122
123                                 if (idx == 0)
124                                         return -EINVAL;
125
126                                 a->sockaddr.in6.sin6_family = AF_INET6;
127                                 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
128                                 a->sockaddr.in6.sin6_scope_id = idx;
129                                 a->sockaddr.in6.sin6_addr = in6addr_any;
130                                 a->size = sizeof(struct sockaddr_in6);
131
132                         }
133                 } else {
134
135                         /* Just a port */
136                         if ((r = safe_atou(s, &u)) < 0)
137                                 return r;
138
139                         if (u <= 0 || u > 0xFFFF)
140                                 return -EINVAL;
141
142                         a->sockaddr.in6.sin6_family = AF_INET6;
143                         a->sockaddr.in6.sin6_port = htons((uint16_t) u);
144                         a->sockaddr.in6.sin6_addr = in6addr_any;
145                         a->size = sizeof(struct sockaddr_in6);
146                 }
147         }
148
149         return 0;
150 }
151
152 int socket_address_verify(const SocketAddress *a) {
153         assert(a);
154
155         switch (socket_address_family(a)) {
156                 case AF_INET:
157                         if (a->size != sizeof(struct sockaddr_in))
158                                 return -EINVAL;
159
160                         if (a->sockaddr.in4.sin_port == 0)
161                                 return -EINVAL;
162
163                         return 0;
164
165                 case AF_INET6:
166                         if (a->size != sizeof(struct sockaddr_in6))
167                                 return -EINVAL;
168
169                         if (a->sockaddr.in6.sin6_port == 0)
170                                 return -EINVAL;
171
172                         return 0;
173
174                 case AF_UNIX:
175                         if (a->size < sizeof(sa_family_t))
176                                 return -EINVAL;
177
178                         if (a->size > sizeof(sa_family_t)) {
179
180                                 if (a->sockaddr.un.sun_path[0] == 0) {
181                                         /* abstract */
182                                         if (a->size != sizeof(struct sockaddr_un))
183                                                 return -EINVAL;
184                                 } else {
185                                         char *e;
186
187                                         /* path */
188                                         if (!(e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path))))
189                                                 return -EINVAL;
190
191                                         if (a->size != sizeof(sa_family_t) + (e - a->sockaddr.un.sun_path) + 1)
192                                                 return -EINVAL;
193                                 }
194                         }
195
196                         return 0;
197
198                 default:
199                         return -EAFNOSUPPORT;
200         }
201 }
202
203 int socket_address_print(const SocketAddress *a, char **p) {
204         int r;
205         assert(a);
206         assert(p);
207
208         if ((r = socket_address_verify(a)) < 0)
209                 return r;
210
211         switch (socket_address_family(a)) {
212                 case AF_INET: {
213                         char *ret;
214
215                         if (!(ret = new(char, INET_ADDRSTRLEN+1+5+1)))
216                                 return -ENOMEM;
217
218                         if (!inet_ntop(AF_INET, &a->sockaddr.in4.sin_addr, ret, INET_ADDRSTRLEN)) {
219                                 free(ret);
220                                 return -errno;
221                         }
222
223                         sprintf(strchr(ret, 0), ":%u", ntohs(a->sockaddr.in4.sin_port));
224                         *p = ret;
225                         return 0;
226                 }
227
228                 case AF_INET6: {
229                         char *ret;
230
231                         if (!(ret = new(char, 1+INET6_ADDRSTRLEN+2+5+1)))
232                                 return -ENOMEM;
233
234                         ret[0] = '[';
235                         if (!inet_ntop(AF_INET6, &a->sockaddr.in6.sin6_addr, ret+1, INET6_ADDRSTRLEN)) {
236                                 free(ret);
237                                 return -errno;
238                         }
239
240                         sprintf(strchr(ret, 0), "]:%u", ntohs(a->sockaddr.in6.sin6_port));
241                         *p = ret;
242                         return 0;
243                 }
244
245                 case AF_UNIX: {
246                         char *ret;
247
248                         if (a->size <= sizeof(sa_family_t)) {
249
250                                 if (!(ret = strdup("<unamed>")))
251                                         return -ENOMEM;
252
253                         } else if (a->sockaddr.un.sun_path[0] == 0) {
254                                 /* abstract */
255
256                                 /* FIXME: We assume we can print the
257                                  * socket path here and that it hasn't
258                                  * more than one NUL byte. That is
259                                  * actually an invalid assumption */
260
261                                 if (!(ret = new(char, sizeof(a->sockaddr.un.sun_path)+1)))
262                                         return -ENOMEM;
263
264                                 ret[0] = '@';
265                                 memcpy(ret+1, a->sockaddr.un.sun_path+1, sizeof(a->sockaddr.un.sun_path)-1);
266                                 ret[sizeof(a->sockaddr.un.sun_path)] = 0;
267
268                         } else {
269
270                                 if (!(ret = strdup(a->sockaddr.un.sun_path)))
271                                         return -ENOMEM;
272                         }
273
274                         *p = ret;
275                         return 0;
276                 }
277
278                 default:
279                         return -EINVAL;
280         }
281 }
282
283 int socket_address_listen(const SocketAddress *a, int backlog, SocketAddressBindIPv6Only only, const char *bind_to_device, int *ret) {
284         int r, fd, one;
285         assert(a);
286         assert(ret);
287
288         if ((r = socket_address_verify(a)) < 0)
289                 return r;
290
291         if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0)
292                 return -errno;
293
294         if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
295                 int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
296
297                 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
298                         goto fail;
299         }
300
301         if (bind_to_device)
302                 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0)
303                         goto fail;
304
305         one = 1;
306         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
307                 goto fail;
308
309         if (bind(fd, &a->sockaddr.sa, a->size) < 0)
310                 goto fail;
311
312         if (a->type == SOCK_STREAM)
313                 if (listen(fd, backlog) < 0)
314                         goto fail;
315
316         *ret = fd;
317         return 0;
318
319 fail:
320         r = -errno;
321         close_nointr(fd);
322         return r;
323 }