chiark / gitweb /
journal, shared: fix warnings during compilation on 32 bits
[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                         r = safe_atou(s, &u);
171                         if (r < 0)
172                                 return r;
173
174                         if (u <= 0 || u > 0xFFFF)
175                                 return -EINVAL;
176
177                         if (socket_ipv6_is_supported()) {
178                                 a->sockaddr.in6.sin6_family = AF_INET6;
179                                 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
180                                 a->sockaddr.in6.sin6_addr = in6addr_any;
181                                 a->size = sizeof(struct sockaddr_in6);
182                         } else {
183                                 a->sockaddr.in4.sin_family = AF_INET;
184                                 a->sockaddr.in4.sin_port = htons((uint16_t) u);
185                                 a->sockaddr.in4.sin_addr.s_addr = INADDR_ANY;
186                                 a->size = sizeof(struct sockaddr_in);
187                         }
188                 }
189         }
190
191         return 0;
192 }
193
194 int socket_address_parse_netlink(SocketAddress *a, const char *s) {
195         int family;
196         unsigned group = 0;
197         _cleanup_free_ char *sfamily = NULL;
198         assert(a);
199         assert(s);
200
201         zero(*a);
202         a->type = SOCK_RAW;
203
204         errno = 0;
205         if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
206                 return errno ? -errno : -EINVAL;
207
208         family = netlink_family_from_string(sfamily);
209         if (family < 0)
210                 return -EINVAL;
211
212         a->sockaddr.nl.nl_family = AF_NETLINK;
213         a->sockaddr.nl.nl_groups = group;
214
215         a->type = SOCK_RAW;
216         a->size = sizeof(struct sockaddr_nl);
217         a->protocol = family;
218
219         return 0;
220 }
221
222 int socket_address_verify(const SocketAddress *a) {
223         assert(a);
224
225         switch (socket_address_family(a)) {
226
227         case AF_INET:
228                 if (a->size != sizeof(struct sockaddr_in))
229                         return -EINVAL;
230
231                 if (a->sockaddr.in4.sin_port == 0)
232                         return -EINVAL;
233
234                 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
235                         return -EINVAL;
236
237                 return 0;
238
239         case AF_INET6:
240                 if (a->size != sizeof(struct sockaddr_in6))
241                         return -EINVAL;
242
243                 if (a->sockaddr.in6.sin6_port == 0)
244                         return -EINVAL;
245
246                 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
247                         return -EINVAL;
248
249                 return 0;
250
251         case AF_UNIX:
252                 if (a->size < offsetof(struct sockaddr_un, sun_path))
253                         return -EINVAL;
254
255                 if (a->size > offsetof(struct sockaddr_un, sun_path)) {
256
257                         if (a->sockaddr.un.sun_path[0] != 0) {
258                                 char *e;
259
260                                 /* path */
261                                 if (!(e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path))))
262                                         return -EINVAL;
263
264                                 if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
265                                         return -EINVAL;
266                         }
267                 }
268
269                 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
270                         return -EINVAL;
271
272                 return 0;
273
274         case AF_NETLINK:
275
276                 if (a->size != sizeof(struct sockaddr_nl))
277                         return -EINVAL;
278
279                 if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
280                         return -EINVAL;
281
282                 return 0;
283
284         default:
285                 return -EAFNOSUPPORT;
286         }
287 }
288
289 int socket_address_print(const SocketAddress *a, char **p) {
290         int r;
291         assert(a);
292         assert(p);
293
294         if ((r = socket_address_verify(a)) < 0)
295                 return r;
296
297         switch (socket_address_family(a)) {
298
299         case AF_INET: {
300                 char *ret;
301
302                 if (!(ret = new(char, INET_ADDRSTRLEN+1+5+1)))
303                         return -ENOMEM;
304
305                 if (!inet_ntop(AF_INET, &a->sockaddr.in4.sin_addr, ret, INET_ADDRSTRLEN)) {
306                         free(ret);
307                         return -errno;
308                 }
309
310                 sprintf(strchr(ret, 0), ":%u", ntohs(a->sockaddr.in4.sin_port));
311                 *p = ret;
312                 return 0;
313         }
314
315         case AF_INET6: {
316                 char *ret;
317
318                 if (!(ret = new(char, 1+INET6_ADDRSTRLEN+2+5+1)))
319                         return -ENOMEM;
320
321                 ret[0] = '[';
322                 if (!inet_ntop(AF_INET6, &a->sockaddr.in6.sin6_addr, ret+1, INET6_ADDRSTRLEN)) {
323                         free(ret);
324                         return -errno;
325                 }
326
327                 sprintf(strchr(ret, 0), "]:%u", ntohs(a->sockaddr.in6.sin6_port));
328                 *p = ret;
329                 return 0;
330         }
331
332         case AF_UNIX: {
333                 char *ret;
334
335                 if (a->size <= offsetof(struct sockaddr_un, sun_path)) {
336
337                         if (!(ret = strdup("<unnamed>")))
338                                 return -ENOMEM;
339
340                 } else if (a->sockaddr.un.sun_path[0] == 0) {
341                         /* abstract */
342
343                         /* FIXME: We assume we can print the
344                          * socket path here and that it hasn't
345                          * more than one NUL byte. That is
346                          * actually an invalid assumption */
347
348                         if (!(ret = new(char, sizeof(a->sockaddr.un.sun_path)+1)))
349                                 return -ENOMEM;
350
351                         ret[0] = '@';
352                         memcpy(ret+1, a->sockaddr.un.sun_path+1, sizeof(a->sockaddr.un.sun_path)-1);
353                         ret[sizeof(a->sockaddr.un.sun_path)] = 0;
354
355                 } else {
356
357                         if (!(ret = strdup(a->sockaddr.un.sun_path)))
358                                 return -ENOMEM;
359                 }
360
361                 *p = ret;
362                 return 0;
363         }
364
365         case AF_NETLINK: {
366                 char _cleanup_free_ *sfamily = NULL;
367
368                 r = netlink_family_to_string_alloc(a->protocol, &sfamily);
369                 if (r < 0)
370                         return r;
371                 r = asprintf(p, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
372
373                 return 0;
374         }
375
376         default:
377                 return -EINVAL;
378         }
379 }
380
381 bool socket_address_can_accept(const SocketAddress *a) {
382         assert(a);
383
384         return
385                 a->type == SOCK_STREAM ||
386                 a->type == SOCK_SEQPACKET;
387 }
388
389 bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
390         assert(a);
391         assert(b);
392
393         /* Invalid addresses are unequal to all */
394         if (socket_address_verify(a) < 0 ||
395             socket_address_verify(b) < 0)
396                 return false;
397
398         if (a->type != b->type)
399                 return false;
400
401         if (a->size != b->size)
402                 return false;
403
404         if (socket_address_family(a) != socket_address_family(b))
405                 return false;
406
407         switch (socket_address_family(a)) {
408
409         case AF_INET:
410                 if (a->sockaddr.in4.sin_addr.s_addr != b->sockaddr.in4.sin_addr.s_addr)
411                         return false;
412
413                 if (a->sockaddr.in4.sin_port != b->sockaddr.in4.sin_port)
414                         return false;
415
416                 break;
417
418         case AF_INET6:
419                 if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
420                         return false;
421
422                 if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
423                         return false;
424
425                 break;
426
427         case AF_UNIX:
428
429                 if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
430                         return false;
431
432                 if (a->sockaddr.un.sun_path[0]) {
433                         if (strncmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0)
434                                 return false;
435                 } else {
436                         if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
437                                 return false;
438                 }
439
440                 break;
441
442         case AF_NETLINK:
443
444                 if (a->protocol != b->protocol)
445                         return false;
446
447                 if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
448                         return false;
449
450                 break;
451
452         default:
453                 /* Cannot compare, so we assume the addresses are different */
454                 return false;
455         }
456
457         return true;
458 }
459
460 bool socket_address_is(const SocketAddress *a, const char *s, int type) {
461         struct SocketAddress b;
462
463         assert(a);
464         assert(s);
465
466         if (socket_address_parse(&b, s) < 0)
467                 return false;
468
469         b.type = type;
470
471         return socket_address_equal(a, &b);
472 }
473
474 bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
475         struct SocketAddress b;
476
477         assert(a);
478         assert(s);
479
480         if (socket_address_parse_netlink(&b, s) < 0)
481                 return false;
482
483         return socket_address_equal(a, &b);
484 }
485
486 bool socket_address_needs_mount(const SocketAddress *a, const char *prefix) {
487         assert(a);
488
489         if (socket_address_family(a) != AF_UNIX)
490                 return false;
491
492         if (a->sockaddr.un.sun_path[0] == 0)
493                 return false;
494
495         return path_startswith(a->sockaddr.un.sun_path, prefix);
496 }
497
498 bool socket_ipv6_is_supported(void) {
499         char *l = 0;
500         bool enabled;
501
502         if (access("/sys/module/ipv6", F_OK) != 0)
503                 return 0;
504
505         /* If we can't check "disable" parameter, assume enabled */
506         if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
507                 return 1;
508
509         /* If module was loaded with disable=1 no IPv6 available */
510         enabled = l[0] == '0';
511         free(l);
512
513         return enabled;
514 }
515
516 static const char* const netlink_family_table[] = {
517         [NETLINK_ROUTE] = "route",
518         [NETLINK_FIREWALL] = "firewall",
519         [NETLINK_INET_DIAG] = "inet-diag",
520         [NETLINK_NFLOG] = "nflog",
521         [NETLINK_XFRM] = "xfrm",
522         [NETLINK_SELINUX] = "selinux",
523         [NETLINK_ISCSI] = "iscsi",
524         [NETLINK_AUDIT] = "audit",
525         [NETLINK_FIB_LOOKUP] = "fib-lookup",
526         [NETLINK_CONNECTOR] = "connector",
527         [NETLINK_NETFILTER] = "netfilter",
528         [NETLINK_IP6_FW] = "ip6-fw",
529         [NETLINK_DNRTMSG] = "dnrtmsg",
530         [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
531         [NETLINK_GENERIC] = "generic",
532         [NETLINK_SCSITRANSPORT] = "scsitransport",
533         [NETLINK_ECRYPTFS] = "ecryptfs"
534 };
535
536 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX);
537
538 static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
539         [SOCKET_ADDRESS_DEFAULT] = "default",
540         [SOCKET_ADDRESS_BOTH] = "both",
541         [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
542 };
543
544 DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);