chiark / gitweb /
build-sys: fix built with --disable-logind
[elogind.git] / src / shared / socket-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <assert.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <arpa/inet.h>
28 #include <stdio.h>
29 #include <net/if.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <stddef.h>
33 #include <sys/ioctl.h>
34
35 #include "macro.h"
36 #include "util.h"
37 #include "mkdir.h"
38 #include "path-util.h"
39 #include "socket-util.h"
40 #include "missing.h"
41
42 int socket_address_parse(SocketAddress *a, const char *s) {
43         int r;
44         char *e, *n;
45         unsigned u;
46
47         assert(a);
48         assert(s);
49
50         zero(*a);
51         a->type = SOCK_STREAM;
52
53         if (*s == '[') {
54                 /* IPv6 in [x:.....:z]:p notation */
55
56                 if (!socket_ipv6_is_supported()) {
57                         log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
58                         return -EAFNOSUPPORT;
59                 }
60
61                 if (!(e = strchr(s+1, ']')))
62                         return -EINVAL;
63
64                 if (!(n = strndup(s+1, e-s-1)))
65                         return -ENOMEM;
66
67                 errno = 0;
68                 if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) {
69                         free(n);
70                         return errno != 0 ? -errno : -EINVAL;
71                 }
72
73                 free(n);
74
75                 e++;
76                 if (*e != ':')
77                         return -EINVAL;
78
79                 e++;
80                 if ((r = safe_atou(e, &u)) < 0)
81                         return r;
82
83                 if (u <= 0 || u > 0xFFFF)
84                         return -EINVAL;
85
86                 a->sockaddr.in6.sin6_family = AF_INET6;
87                 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
88                 a->size = sizeof(struct sockaddr_in6);
89
90         } else if (*s == '/') {
91                 /* AF_UNIX socket */
92
93                 size_t l;
94
95                 l = strlen(s);
96                 if (l >= sizeof(a->sockaddr.un.sun_path))
97                         return -EINVAL;
98
99                 a->sockaddr.un.sun_family = AF_UNIX;
100                 memcpy(a->sockaddr.un.sun_path, s, l);
101                 a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
102
103         } else if (*s == '@') {
104                 /* Abstract AF_UNIX socket */
105                 size_t l;
106
107                 l = strlen(s+1);
108                 if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
109                         return -EINVAL;
110
111                 a->sockaddr.un.sun_family = AF_UNIX;
112                 memcpy(a->sockaddr.un.sun_path+1, s+1, l);
113                 a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
114
115         } else {
116
117                 if ((e = strchr(s, ':'))) {
118
119                         if ((r = safe_atou(e+1, &u)) < 0)
120                                 return r;
121
122                         if (u <= 0 || u > 0xFFFF)
123                                 return -EINVAL;
124
125                         if (!(n = strndup(s, e-s)))
126                                 return -ENOMEM;
127
128                         /* IPv4 in w.x.y.z:p notation? */
129                         if ((r = inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr)) < 0) {
130                                 free(n);
131                                 return -errno;
132                         }
133
134                         if (r > 0) {
135                                 /* Gotcha, it's a traditional IPv4 address */
136                                 free(n);
137
138                                 a->sockaddr.in4.sin_family = AF_INET;
139                                 a->sockaddr.in4.sin_port = htons((uint16_t) u);
140                                 a->size = sizeof(struct sockaddr_in);
141                         } else {
142                                 unsigned idx;
143
144                                 if (strlen(n) > IF_NAMESIZE-1) {
145                                         free(n);
146                                         return -EINVAL;
147                                 }
148
149                                 /* Uh, our last resort, an interface name */
150                                 idx = if_nametoindex(n);
151                                 free(n);
152
153                                 if (idx == 0)
154                                         return -EINVAL;
155
156                                 if (!socket_ipv6_is_supported()) {
157                                         log_warning("Binding to interface is not available since kernel does not support IPv6.");
158                                         return -EAFNOSUPPORT;
159                                 }
160
161                                 a->sockaddr.in6.sin6_family = AF_INET6;
162                                 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
163                                 a->sockaddr.in6.sin6_scope_id = idx;
164                                 a->sockaddr.in6.sin6_addr = in6addr_any;
165                                 a->size = sizeof(struct sockaddr_in6);
166                         }
167                 } else {
168
169                         /* Just a port */
170                         if ((r = safe_atou(s, &u)) < 0)
171                                 return r;
172
173                         if (u <= 0 || u > 0xFFFF)
174                                 return -EINVAL;
175
176                         if (socket_ipv6_is_supported()) {
177                                 a->sockaddr.in6.sin6_family = AF_INET6;
178                                 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
179                                 a->sockaddr.in6.sin6_addr = in6addr_any;
180                                 a->size = sizeof(struct sockaddr_in6);
181                         } else {
182                                 a->sockaddr.in4.sin_family = AF_INET;
183                                 a->sockaddr.in4.sin_port = htons((uint16_t) u);
184                                 a->sockaddr.in4.sin_addr.s_addr = INADDR_ANY;
185                                 a->size = sizeof(struct sockaddr_in);
186                         }
187                 }
188         }
189
190         return 0;
191 }
192
193 int socket_address_parse_netlink(SocketAddress *a, const char *s) {
194         int family;
195         unsigned group = 0;
196         char* sfamily = NULL;
197         assert(a);
198         assert(s);
199
200         zero(*a);
201         a->type = SOCK_RAW;
202
203         errno = 0;
204         if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
205                 return errno ? -errno : -EINVAL;
206
207         if ((family = netlink_family_from_string(sfamily)) < 0)
208                 if (safe_atoi(sfamily, &family) < 0) {
209                         free(sfamily);
210                         return -EINVAL;
211                 }
212
213         free(sfamily);
214
215         a->sockaddr.nl.nl_family = AF_NETLINK;
216         a->sockaddr.nl.nl_groups = group;
217
218         a->type = SOCK_RAW;
219         a->size = sizeof(struct sockaddr_nl);
220         a->protocol = family;
221
222         return 0;
223 }
224
225 int socket_address_verify(const SocketAddress *a) {
226         assert(a);
227
228         switch (socket_address_family(a)) {
229
230         case AF_INET:
231                 if (a->size != sizeof(struct sockaddr_in))
232                         return -EINVAL;
233
234                 if (a->sockaddr.in4.sin_port == 0)
235                         return -EINVAL;
236
237                 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
238                         return -EINVAL;
239
240                 return 0;
241
242         case AF_INET6:
243                 if (a->size != sizeof(struct sockaddr_in6))
244                         return -EINVAL;
245
246                 if (a->sockaddr.in6.sin6_port == 0)
247                         return -EINVAL;
248
249                 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
250                         return -EINVAL;
251
252                 return 0;
253
254         case AF_UNIX:
255                 if (a->size < offsetof(struct sockaddr_un, sun_path))
256                         return -EINVAL;
257
258                 if (a->size > offsetof(struct sockaddr_un, sun_path)) {
259
260                         if (a->sockaddr.un.sun_path[0] != 0) {
261                                 char *e;
262
263                                 /* path */
264                                 if (!(e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path))))
265                                         return -EINVAL;
266
267                                 if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
268                                         return -EINVAL;
269                         }
270                 }
271
272                 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
273                         return -EINVAL;
274
275                 return 0;
276
277         case AF_NETLINK:
278
279                 if (a->size != sizeof(struct sockaddr_nl))
280                         return -EINVAL;
281
282                 if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
283                         return -EINVAL;
284
285                 return 0;
286
287         default:
288                 return -EAFNOSUPPORT;
289         }
290 }
291
292 int socket_address_print(const SocketAddress *a, char **p) {
293         int r;
294         assert(a);
295         assert(p);
296
297         if ((r = socket_address_verify(a)) < 0)
298                 return r;
299
300         switch (socket_address_family(a)) {
301
302         case AF_INET: {
303                 char *ret;
304
305                 if (!(ret = new(char, INET_ADDRSTRLEN+1+5+1)))
306                         return -ENOMEM;
307
308                 if (!inet_ntop(AF_INET, &a->sockaddr.in4.sin_addr, ret, INET_ADDRSTRLEN)) {
309                         free(ret);
310                         return -errno;
311                 }
312
313                 sprintf(strchr(ret, 0), ":%u", ntohs(a->sockaddr.in4.sin_port));
314                 *p = ret;
315                 return 0;
316         }
317
318         case AF_INET6: {
319                 char *ret;
320
321                 if (!(ret = new(char, 1+INET6_ADDRSTRLEN+2+5+1)))
322                         return -ENOMEM;
323
324                 ret[0] = '[';
325                 if (!inet_ntop(AF_INET6, &a->sockaddr.in6.sin6_addr, ret+1, INET6_ADDRSTRLEN)) {
326                         free(ret);
327                         return -errno;
328                 }
329
330                 sprintf(strchr(ret, 0), "]:%u", ntohs(a->sockaddr.in6.sin6_port));
331                 *p = ret;
332                 return 0;
333         }
334
335         case AF_UNIX: {
336                 char *ret;
337
338                 if (a->size <= offsetof(struct sockaddr_un, sun_path)) {
339
340                         if (!(ret = strdup("<unnamed>")))
341                                 return -ENOMEM;
342
343                 } else if (a->sockaddr.un.sun_path[0] == 0) {
344                         /* abstract */
345
346                         /* FIXME: We assume we can print the
347                          * socket path here and that it hasn't
348                          * more than one NUL byte. That is
349                          * actually an invalid assumption */
350
351                         if (!(ret = new(char, sizeof(a->sockaddr.un.sun_path)+1)))
352                                 return -ENOMEM;
353
354                         ret[0] = '@';
355                         memcpy(ret+1, a->sockaddr.un.sun_path+1, sizeof(a->sockaddr.un.sun_path)-1);
356                         ret[sizeof(a->sockaddr.un.sun_path)] = 0;
357
358                 } else {
359
360                         if (!(ret = strdup(a->sockaddr.un.sun_path)))
361                                 return -ENOMEM;
362                 }
363
364                 *p = ret;
365                 return 0;
366         }
367
368         case AF_NETLINK: {
369                 const char *sfamily;
370
371                 if ((sfamily = netlink_family_to_string(a->protocol)))
372                         r = asprintf(p, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
373                 else
374                         r = asprintf(p, "%i %u", a->protocol, a->sockaddr.nl.nl_groups);
375
376                 if (r < 0)
377                         return -ENOMEM;
378
379                 return 0;
380         }
381
382         default:
383                 return -EINVAL;
384         }
385 }
386
387 bool socket_address_can_accept(const SocketAddress *a) {
388         assert(a);
389
390         return
391                 a->type == SOCK_STREAM ||
392                 a->type == SOCK_SEQPACKET;
393 }
394
395 bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
396         assert(a);
397         assert(b);
398
399         /* Invalid addresses are unequal to all */
400         if (socket_address_verify(a) < 0 ||
401             socket_address_verify(b) < 0)
402                 return false;
403
404         if (a->type != b->type)
405                 return false;
406
407         if (a->size != b->size)
408                 return false;
409
410         if (socket_address_family(a) != socket_address_family(b))
411                 return false;
412
413         switch (socket_address_family(a)) {
414
415         case AF_INET:
416                 if (a->sockaddr.in4.sin_addr.s_addr != b->sockaddr.in4.sin_addr.s_addr)
417                         return false;
418
419                 if (a->sockaddr.in4.sin_port != b->sockaddr.in4.sin_port)
420                         return false;
421
422                 break;
423
424         case AF_INET6:
425                 if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
426                         return false;
427
428                 if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
429                         return false;
430
431                 break;
432
433         case AF_UNIX:
434
435                 if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
436                         return false;
437
438                 if (a->sockaddr.un.sun_path[0]) {
439                         if (strncmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0)
440                                 return false;
441                 } else {
442                         if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
443                                 return false;
444                 }
445
446                 break;
447
448         case AF_NETLINK:
449
450                 if (a->protocol != b->protocol)
451                         return false;
452
453                 if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
454                         return false;
455
456                 break;
457
458         default:
459                 /* Cannot compare, so we assume the addresses are different */
460                 return false;
461         }
462
463         return true;
464 }
465
466 bool socket_address_is(const SocketAddress *a, const char *s, int type) {
467         struct SocketAddress b;
468
469         assert(a);
470         assert(s);
471
472         if (socket_address_parse(&b, s) < 0)
473                 return false;
474
475         b.type = type;
476
477         return socket_address_equal(a, &b);
478 }
479
480 bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
481         struct SocketAddress b;
482
483         assert(a);
484         assert(s);
485
486         if (socket_address_parse_netlink(&b, s) < 0)
487                 return false;
488
489         return socket_address_equal(a, &b);
490 }
491
492 bool socket_address_needs_mount(const SocketAddress *a, const char *prefix) {
493         assert(a);
494
495         if (socket_address_family(a) != AF_UNIX)
496                 return false;
497
498         if (a->sockaddr.un.sun_path[0] == 0)
499                 return false;
500
501         return path_startswith(a->sockaddr.un.sun_path, prefix);
502 }
503
504 bool socket_ipv6_is_supported(void) {
505         char *l = 0;
506         bool enabled;
507
508         if (access("/sys/module/ipv6", F_OK) != 0)
509                 return 0;
510
511         /* If we can't check "disable" parameter, assume enabled */
512         if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
513                 return 1;
514
515         /* If module was loaded with disable=1 no IPv6 available */
516         enabled = l[0] == '0';
517         free(l);
518
519         return enabled;
520 }
521
522 static const char* const netlink_family_table[] = {
523         [NETLINK_ROUTE] = "route",
524         [NETLINK_FIREWALL] = "firewall",
525         [NETLINK_INET_DIAG] = "inet-diag",
526         [NETLINK_NFLOG] = "nflog",
527         [NETLINK_XFRM] = "xfrm",
528         [NETLINK_SELINUX] = "selinux",
529         [NETLINK_ISCSI] = "iscsi",
530         [NETLINK_AUDIT] = "audit",
531         [NETLINK_FIB_LOOKUP] = "fib-lookup",
532         [NETLINK_CONNECTOR] = "connector",
533         [NETLINK_NETFILTER] = "netfilter",
534         [NETLINK_IP6_FW] = "ip6-fw",
535         [NETLINK_DNRTMSG] = "dnrtmsg",
536         [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
537         [NETLINK_GENERIC] = "generic",
538         [NETLINK_SCSITRANSPORT] = "scsitransport",
539         [NETLINK_ECRYPTFS] = "ecryptfs"
540 };
541
542 DEFINE_STRING_TABLE_LOOKUP(netlink_family, int);
543
544 static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
545         [SOCKET_ADDRESS_DEFAULT] = "default",
546         [SOCKET_ADDRESS_BOTH] = "both",
547         [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
548 };
549
550 DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);