]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/socket-util.c
socket-util: added check of return value
[thirdparty/systemd.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 if (r < 0)
373 return -ENOMEM;
374
375 return 0;
376 }
377
378 default:
379 return -EINVAL;
380 }
381 }
382
383 bool socket_address_can_accept(const SocketAddress *a) {
384 assert(a);
385
386 return
387 a->type == SOCK_STREAM ||
388 a->type == SOCK_SEQPACKET;
389 }
390
391 bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
392 assert(a);
393 assert(b);
394
395 /* Invalid addresses are unequal to all */
396 if (socket_address_verify(a) < 0 ||
397 socket_address_verify(b) < 0)
398 return false;
399
400 if (a->type != b->type)
401 return false;
402
403 if (a->size != b->size)
404 return false;
405
406 if (socket_address_family(a) != socket_address_family(b))
407 return false;
408
409 switch (socket_address_family(a)) {
410
411 case AF_INET:
412 if (a->sockaddr.in4.sin_addr.s_addr != b->sockaddr.in4.sin_addr.s_addr)
413 return false;
414
415 if (a->sockaddr.in4.sin_port != b->sockaddr.in4.sin_port)
416 return false;
417
418 break;
419
420 case AF_INET6:
421 if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
422 return false;
423
424 if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
425 return false;
426
427 break;
428
429 case AF_UNIX:
430
431 if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
432 return false;
433
434 if (a->sockaddr.un.sun_path[0]) {
435 if (strncmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0)
436 return false;
437 } else {
438 if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
439 return false;
440 }
441
442 break;
443
444 case AF_NETLINK:
445
446 if (a->protocol != b->protocol)
447 return false;
448
449 if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
450 return false;
451
452 break;
453
454 default:
455 /* Cannot compare, so we assume the addresses are different */
456 return false;
457 }
458
459 return true;
460 }
461
462 bool socket_address_is(const SocketAddress *a, const char *s, int type) {
463 struct SocketAddress b;
464
465 assert(a);
466 assert(s);
467
468 if (socket_address_parse(&b, s) < 0)
469 return false;
470
471 b.type = type;
472
473 return socket_address_equal(a, &b);
474 }
475
476 bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
477 struct SocketAddress b;
478
479 assert(a);
480 assert(s);
481
482 if (socket_address_parse_netlink(&b, s) < 0)
483 return false;
484
485 return socket_address_equal(a, &b);
486 }
487
488 bool socket_address_needs_mount(const SocketAddress *a, const char *prefix) {
489 assert(a);
490
491 if (socket_address_family(a) != AF_UNIX)
492 return false;
493
494 if (a->sockaddr.un.sun_path[0] == 0)
495 return false;
496
497 return path_startswith(a->sockaddr.un.sun_path, prefix);
498 }
499
500 bool socket_ipv6_is_supported(void) {
501 char *l = 0;
502 bool enabled;
503
504 if (access("/sys/module/ipv6", F_OK) != 0)
505 return 0;
506
507 /* If we can't check "disable" parameter, assume enabled */
508 if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
509 return 1;
510
511 /* If module was loaded with disable=1 no IPv6 available */
512 enabled = l[0] == '0';
513 free(l);
514
515 return enabled;
516 }
517
518 static const char* const netlink_family_table[] = {
519 [NETLINK_ROUTE] = "route",
520 [NETLINK_FIREWALL] = "firewall",
521 [NETLINK_INET_DIAG] = "inet-diag",
522 [NETLINK_NFLOG] = "nflog",
523 [NETLINK_XFRM] = "xfrm",
524 [NETLINK_SELINUX] = "selinux",
525 [NETLINK_ISCSI] = "iscsi",
526 [NETLINK_AUDIT] = "audit",
527 [NETLINK_FIB_LOOKUP] = "fib-lookup",
528 [NETLINK_CONNECTOR] = "connector",
529 [NETLINK_NETFILTER] = "netfilter",
530 [NETLINK_IP6_FW] = "ip6-fw",
531 [NETLINK_DNRTMSG] = "dnrtmsg",
532 [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
533 [NETLINK_GENERIC] = "generic",
534 [NETLINK_SCSITRANSPORT] = "scsitransport",
535 [NETLINK_ECRYPTFS] = "ecryptfs"
536 };
537
538 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX);
539
540 static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
541 [SOCKET_ADDRESS_DEFAULT] = "default",
542 [SOCKET_ADDRESS_BOTH] = "both",
543 [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
544 };
545
546 DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);