]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/socket-util.c
socket-util: move remaining socket-related calls from util.[ch] to socket-util.[ch]
[thirdparty/systemd.git] / src / basic / 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 <arpa/inet.h>
23 #include <errno.h>
24 #include <net/if.h>
25 #include <netdb.h>
26 #include <netinet/ip.h>
27 #include <stddef.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32
33 #include "fd-util.h"
34 #include "fileio.h"
35 #include "formats-util.h"
36 #include "macro.h"
37 #include "missing.h"
38 #include "path-util.h"
39 #include "socket-util.h"
40 #include "string-util.h"
41 #include "util.h"
42
43 int socket_address_parse(SocketAddress *a, const char *s) {
44 char *e, *n;
45 unsigned u;
46 int r;
47
48 assert(a);
49 assert(s);
50
51 zero(*a);
52 a->type = SOCK_STREAM;
53
54 if (*s == '[') {
55 /* IPv6 in [x:.....:z]:p notation */
56
57 e = strchr(s+1, ']');
58 if (!e)
59 return -EINVAL;
60
61 n = strndupa(s+1, e-s-1);
62
63 errno = 0;
64 if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
65 return errno > 0 ? -errno : -EINVAL;
66
67 e++;
68 if (*e != ':')
69 return -EINVAL;
70
71 e++;
72 r = safe_atou(e, &u);
73 if (r < 0)
74 return r;
75
76 if (u <= 0 || u > 0xFFFF)
77 return -EINVAL;
78
79 a->sockaddr.in6.sin6_family = AF_INET6;
80 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
81 a->size = sizeof(struct sockaddr_in6);
82
83 } else if (*s == '/') {
84 /* AF_UNIX socket */
85
86 size_t l;
87
88 l = strlen(s);
89 if (l >= sizeof(a->sockaddr.un.sun_path))
90 return -EINVAL;
91
92 a->sockaddr.un.sun_family = AF_UNIX;
93 memcpy(a->sockaddr.un.sun_path, s, l);
94 a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
95
96 } else if (*s == '@') {
97 /* Abstract AF_UNIX socket */
98 size_t l;
99
100 l = strlen(s+1);
101 if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
102 return -EINVAL;
103
104 a->sockaddr.un.sun_family = AF_UNIX;
105 memcpy(a->sockaddr.un.sun_path+1, s+1, l);
106 a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
107
108 } else {
109 e = strchr(s, ':');
110 if (e) {
111 r = safe_atou(e+1, &u);
112 if (r < 0)
113 return r;
114
115 if (u <= 0 || u > 0xFFFF)
116 return -EINVAL;
117
118 n = strndupa(s, e-s);
119
120 /* IPv4 in w.x.y.z:p notation? */
121 r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
122 if (r < 0)
123 return -errno;
124
125 if (r > 0) {
126 /* Gotcha, it's a traditional IPv4 address */
127 a->sockaddr.in.sin_family = AF_INET;
128 a->sockaddr.in.sin_port = htons((uint16_t) u);
129 a->size = sizeof(struct sockaddr_in);
130 } else {
131 unsigned idx;
132
133 if (strlen(n) > IF_NAMESIZE-1)
134 return -EINVAL;
135
136 /* Uh, our last resort, an interface name */
137 idx = if_nametoindex(n);
138 if (idx == 0)
139 return -EINVAL;
140
141 a->sockaddr.in6.sin6_family = AF_INET6;
142 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
143 a->sockaddr.in6.sin6_scope_id = idx;
144 a->sockaddr.in6.sin6_addr = in6addr_any;
145 a->size = sizeof(struct sockaddr_in6);
146 }
147 } else {
148
149 /* Just a port */
150 r = safe_atou(s, &u);
151 if (r < 0)
152 return r;
153
154 if (u <= 0 || u > 0xFFFF)
155 return -EINVAL;
156
157 if (socket_ipv6_is_supported()) {
158 a->sockaddr.in6.sin6_family = AF_INET6;
159 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
160 a->sockaddr.in6.sin6_addr = in6addr_any;
161 a->size = sizeof(struct sockaddr_in6);
162 } else {
163 a->sockaddr.in.sin_family = AF_INET;
164 a->sockaddr.in.sin_port = htons((uint16_t) u);
165 a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
166 a->size = sizeof(struct sockaddr_in);
167 }
168 }
169 }
170
171 return 0;
172 }
173
174 int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
175 SocketAddress b;
176 int r;
177
178 /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
179
180 r = socket_address_parse(&b, s);
181 if (r < 0)
182 return r;
183
184 if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) {
185 log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
186 return -EAFNOSUPPORT;
187 }
188
189 *a = b;
190 return 0;
191 }
192
193 int socket_address_parse_netlink(SocketAddress *a, const char *s) {
194 int family;
195 unsigned group = 0;
196 _cleanup_free_ 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 > 0 ? -errno : -EINVAL;
206
207 family = netlink_family_from_string(sfamily);
208 if (family < 0)
209 return -EINVAL;
210
211 a->sockaddr.nl.nl_family = AF_NETLINK;
212 a->sockaddr.nl.nl_groups = group;
213
214 a->type = SOCK_RAW;
215 a->size = sizeof(struct sockaddr_nl);
216 a->protocol = family;
217
218 return 0;
219 }
220
221 int socket_address_verify(const SocketAddress *a) {
222 assert(a);
223
224 switch (socket_address_family(a)) {
225
226 case AF_INET:
227 if (a->size != sizeof(struct sockaddr_in))
228 return -EINVAL;
229
230 if (a->sockaddr.in.sin_port == 0)
231 return -EINVAL;
232
233 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
234 return -EINVAL;
235
236 return 0;
237
238 case AF_INET6:
239 if (a->size != sizeof(struct sockaddr_in6))
240 return -EINVAL;
241
242 if (a->sockaddr.in6.sin6_port == 0)
243 return -EINVAL;
244
245 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
246 return -EINVAL;
247
248 return 0;
249
250 case AF_UNIX:
251 if (a->size < offsetof(struct sockaddr_un, sun_path))
252 return -EINVAL;
253
254 if (a->size > offsetof(struct sockaddr_un, sun_path)) {
255
256 if (a->sockaddr.un.sun_path[0] != 0) {
257 char *e;
258
259 /* path */
260 e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
261 if (!e)
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 **ret) {
290 int r;
291
292 assert(a);
293 assert(ret);
294
295 r = socket_address_verify(a);
296 if (r < 0)
297 return r;
298
299 if (socket_address_family(a) == AF_NETLINK) {
300 _cleanup_free_ char *sfamily = NULL;
301
302 r = netlink_family_to_string_alloc(a->protocol, &sfamily);
303 if (r < 0)
304 return r;
305
306 r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
307 if (r < 0)
308 return -ENOMEM;
309
310 return 0;
311 }
312
313 return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret);
314 }
315
316 bool socket_address_can_accept(const SocketAddress *a) {
317 assert(a);
318
319 return
320 a->type == SOCK_STREAM ||
321 a->type == SOCK_SEQPACKET;
322 }
323
324 bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
325 assert(a);
326 assert(b);
327
328 /* Invalid addresses are unequal to all */
329 if (socket_address_verify(a) < 0 ||
330 socket_address_verify(b) < 0)
331 return false;
332
333 if (a->type != b->type)
334 return false;
335
336 if (socket_address_family(a) != socket_address_family(b))
337 return false;
338
339 switch (socket_address_family(a)) {
340
341 case AF_INET:
342 if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr)
343 return false;
344
345 if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port)
346 return false;
347
348 break;
349
350 case AF_INET6:
351 if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
352 return false;
353
354 if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
355 return false;
356
357 break;
358
359 case AF_UNIX:
360 if (a->size <= offsetof(struct sockaddr_un, sun_path) ||
361 b->size <= offsetof(struct sockaddr_un, sun_path))
362 return false;
363
364 if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
365 return false;
366
367 if (a->sockaddr.un.sun_path[0]) {
368 if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path))
369 return false;
370 } else {
371 if (a->size != b->size)
372 return false;
373
374 if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
375 return false;
376 }
377
378 break;
379
380 case AF_NETLINK:
381 if (a->protocol != b->protocol)
382 return false;
383
384 if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
385 return false;
386
387 break;
388
389 default:
390 /* Cannot compare, so we assume the addresses are different */
391 return false;
392 }
393
394 return true;
395 }
396
397 bool socket_address_is(const SocketAddress *a, const char *s, int type) {
398 struct SocketAddress b;
399
400 assert(a);
401 assert(s);
402
403 if (socket_address_parse(&b, s) < 0)
404 return false;
405
406 b.type = type;
407
408 return socket_address_equal(a, &b);
409 }
410
411 bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
412 struct SocketAddress b;
413
414 assert(a);
415 assert(s);
416
417 if (socket_address_parse_netlink(&b, s) < 0)
418 return false;
419
420 return socket_address_equal(a, &b);
421 }
422
423 const char* socket_address_get_path(const SocketAddress *a) {
424 assert(a);
425
426 if (socket_address_family(a) != AF_UNIX)
427 return NULL;
428
429 if (a->sockaddr.un.sun_path[0] == 0)
430 return NULL;
431
432 return a->sockaddr.un.sun_path;
433 }
434
435 bool socket_ipv6_is_supported(void) {
436 _cleanup_free_ char *l = NULL;
437
438 if (access("/sys/module/ipv6", F_OK) != 0)
439 return false;
440
441 /* If we can't check "disable" parameter, assume enabled */
442 if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
443 return true;
444
445 /* If module was loaded with disable=1 no IPv6 available */
446 return l[0] == '0';
447 }
448
449 bool socket_address_matches_fd(const SocketAddress *a, int fd) {
450 SocketAddress b;
451 socklen_t solen;
452
453 assert(a);
454 assert(fd >= 0);
455
456 b.size = sizeof(b.sockaddr);
457 if (getsockname(fd, &b.sockaddr.sa, &b.size) < 0)
458 return false;
459
460 if (b.sockaddr.sa.sa_family != a->sockaddr.sa.sa_family)
461 return false;
462
463 solen = sizeof(b.type);
464 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &b.type, &solen) < 0)
465 return false;
466
467 if (b.type != a->type)
468 return false;
469
470 if (a->protocol != 0) {
471 solen = sizeof(b.protocol);
472 if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &b.protocol, &solen) < 0)
473 return false;
474
475 if (b.protocol != a->protocol)
476 return false;
477 }
478
479 return socket_address_equal(a, &b);
480 }
481
482 int sockaddr_port(const struct sockaddr *_sa) {
483 union sockaddr_union *sa = (union sockaddr_union*) _sa;
484
485 assert(sa);
486
487 if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6))
488 return -EAFNOSUPPORT;
489
490 return ntohs(sa->sa.sa_family == AF_INET6 ?
491 sa->in6.sin6_port :
492 sa->in.sin_port);
493 }
494
495 int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
496 union sockaddr_union *sa = (union sockaddr_union*) _sa;
497 char *p;
498 int r;
499
500 assert(sa);
501 assert(salen >= sizeof(sa->sa.sa_family));
502
503 switch (sa->sa.sa_family) {
504
505 case AF_INET: {
506 uint32_t a;
507
508 a = ntohl(sa->in.sin_addr.s_addr);
509
510 if (include_port)
511 r = asprintf(&p,
512 "%u.%u.%u.%u:%u",
513 a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
514 ntohs(sa->in.sin_port));
515 else
516 r = asprintf(&p,
517 "%u.%u.%u.%u",
518 a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF);
519 if (r < 0)
520 return -ENOMEM;
521 break;
522 }
523
524 case AF_INET6: {
525 static const unsigned char ipv4_prefix[] = {
526 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
527 };
528
529 if (translate_ipv6 &&
530 memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
531 const uint8_t *a = sa->in6.sin6_addr.s6_addr+12;
532 if (include_port)
533 r = asprintf(&p,
534 "%u.%u.%u.%u:%u",
535 a[0], a[1], a[2], a[3],
536 ntohs(sa->in6.sin6_port));
537 else
538 r = asprintf(&p,
539 "%u.%u.%u.%u",
540 a[0], a[1], a[2], a[3]);
541 if (r < 0)
542 return -ENOMEM;
543 } else {
544 char a[INET6_ADDRSTRLEN];
545
546 inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
547
548 if (include_port) {
549 r = asprintf(&p,
550 "[%s]:%u",
551 a,
552 ntohs(sa->in6.sin6_port));
553 if (r < 0)
554 return -ENOMEM;
555 } else {
556 p = strdup(a);
557 if (!p)
558 return -ENOMEM;
559 }
560 }
561
562 break;
563 }
564
565 case AF_UNIX:
566 if (salen <= offsetof(struct sockaddr_un, sun_path)) {
567 p = strdup("<unnamed>");
568 if (!p)
569 return -ENOMEM;
570
571 } else if (sa->un.sun_path[0] == 0) {
572 /* abstract */
573
574 /* FIXME: We assume we can print the
575 * socket path here and that it hasn't
576 * more than one NUL byte. That is
577 * actually an invalid assumption */
578
579 p = new(char, sizeof(sa->un.sun_path)+1);
580 if (!p)
581 return -ENOMEM;
582
583 p[0] = '@';
584 memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1);
585 p[sizeof(sa->un.sun_path)] = 0;
586
587 } else {
588 p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path));
589 if (!p)
590 return -ENOMEM;
591 }
592
593 break;
594
595 default:
596 return -EOPNOTSUPP;
597 }
598
599
600 *ret = p;
601 return 0;
602 }
603
604 int getpeername_pretty(int fd, char **ret) {
605 union sockaddr_union sa;
606 socklen_t salen = sizeof(sa);
607 int r;
608
609 assert(fd >= 0);
610 assert(ret);
611
612 if (getpeername(fd, &sa.sa, &salen) < 0)
613 return -errno;
614
615 if (sa.sa.sa_family == AF_UNIX) {
616 struct ucred ucred = {};
617
618 /* UNIX connection sockets are anonymous, so let's use
619 * PID/UID as pretty credentials instead */
620
621 r = getpeercred(fd, &ucred);
622 if (r < 0)
623 return r;
624
625 if (asprintf(ret, "PID "PID_FMT"/UID "UID_FMT, ucred.pid, ucred.uid) < 0)
626 return -ENOMEM;
627
628 return 0;
629 }
630
631 /* For remote sockets we translate IPv6 addresses back to IPv4
632 * if applicable, since that's nicer. */
633
634 return sockaddr_pretty(&sa.sa, salen, true, true, ret);
635 }
636
637 int getsockname_pretty(int fd, char **ret) {
638 union sockaddr_union sa;
639 socklen_t salen = sizeof(sa);
640
641 assert(fd >= 0);
642 assert(ret);
643
644 if (getsockname(fd, &sa.sa, &salen) < 0)
645 return -errno;
646
647 /* For local sockets we do not translate IPv6 addresses back
648 * to IPv6 if applicable, since this is usually used for
649 * listening sockets where the difference between IPv4 and
650 * IPv6 matters. */
651
652 return sockaddr_pretty(&sa.sa, salen, false, true, ret);
653 }
654
655 int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) {
656 int r;
657 char host[NI_MAXHOST], *ret;
658
659 assert(_ret);
660
661 r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0,
662 NI_IDN|NI_IDN_USE_STD3_ASCII_RULES);
663 if (r != 0) {
664 int saved_errno = errno;
665
666 r = sockaddr_pretty(&sa->sa, salen, true, true, &ret);
667 if (r < 0)
668 return r;
669
670 log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret);
671 } else {
672 ret = strdup(host);
673 if (!ret)
674 return -ENOMEM;
675 }
676
677 *_ret = ret;
678 return 0;
679 }
680
681 int getnameinfo_pretty(int fd, char **ret) {
682 union sockaddr_union sa;
683 socklen_t salen = sizeof(sa);
684
685 assert(fd >= 0);
686 assert(ret);
687
688 if (getsockname(fd, &sa.sa, &salen) < 0)
689 return -errno;
690
691 return socknameinfo_pretty(&sa, salen, ret);
692 }
693
694 int socket_address_unlink(SocketAddress *a) {
695 assert(a);
696
697 if (socket_address_family(a) != AF_UNIX)
698 return 0;
699
700 if (a->sockaddr.un.sun_path[0] == 0)
701 return 0;
702
703 if (unlink(a->sockaddr.un.sun_path) < 0)
704 return -errno;
705
706 return 1;
707 }
708
709 static const char* const netlink_family_table[] = {
710 [NETLINK_ROUTE] = "route",
711 [NETLINK_FIREWALL] = "firewall",
712 [NETLINK_INET_DIAG] = "inet-diag",
713 [NETLINK_NFLOG] = "nflog",
714 [NETLINK_XFRM] = "xfrm",
715 [NETLINK_SELINUX] = "selinux",
716 [NETLINK_ISCSI] = "iscsi",
717 [NETLINK_AUDIT] = "audit",
718 [NETLINK_FIB_LOOKUP] = "fib-lookup",
719 [NETLINK_CONNECTOR] = "connector",
720 [NETLINK_NETFILTER] = "netfilter",
721 [NETLINK_IP6_FW] = "ip6-fw",
722 [NETLINK_DNRTMSG] = "dnrtmsg",
723 [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
724 [NETLINK_GENERIC] = "generic",
725 [NETLINK_SCSITRANSPORT] = "scsitransport",
726 [NETLINK_ECRYPTFS] = "ecryptfs"
727 };
728
729 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX);
730
731 static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
732 [SOCKET_ADDRESS_DEFAULT] = "default",
733 [SOCKET_ADDRESS_BOTH] = "both",
734 [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
735 };
736
737 DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
738
739 bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) {
740 assert(a);
741 assert(b);
742
743 if (a->sa.sa_family != b->sa.sa_family)
744 return false;
745
746 if (a->sa.sa_family == AF_INET)
747 return a->in.sin_addr.s_addr == b->in.sin_addr.s_addr;
748
749 if (a->sa.sa_family == AF_INET6)
750 return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0;
751
752 return false;
753 }
754
755 int fd_inc_sndbuf(int fd, size_t n) {
756 int r, value;
757 socklen_t l = sizeof(value);
758
759 r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
760 if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
761 return 0;
762
763 /* If we have the privileges we will ignore the kernel limit. */
764
765 value = (int) n;
766 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
767 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
768 return -errno;
769
770 return 1;
771 }
772
773 int fd_inc_rcvbuf(int fd, size_t n) {
774 int r, value;
775 socklen_t l = sizeof(value);
776
777 r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
778 if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
779 return 0;
780
781 /* If we have the privileges we will ignore the kernel limit. */
782
783 value = (int) n;
784 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
785 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
786 return -errno;
787 return 1;
788 }
789
790 static const char* const ip_tos_table[] = {
791 [IPTOS_LOWDELAY] = "low-delay",
792 [IPTOS_THROUGHPUT] = "throughput",
793 [IPTOS_RELIABILITY] = "reliability",
794 [IPTOS_LOWCOST] = "low-cost",
795 };
796
797 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
798
799 int getpeercred(int fd, struct ucred *ucred) {
800 socklen_t n = sizeof(struct ucred);
801 struct ucred u;
802 int r;
803
804 assert(fd >= 0);
805 assert(ucred);
806
807 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n);
808 if (r < 0)
809 return -errno;
810
811 if (n != sizeof(struct ucred))
812 return -EIO;
813
814 /* Check if the data is actually useful and not suppressed due
815 * to namespacing issues */
816 if (u.pid <= 0)
817 return -ENODATA;
818 if (u.uid == UID_INVALID)
819 return -ENODATA;
820 if (u.gid == GID_INVALID)
821 return -ENODATA;
822
823 *ucred = u;
824 return 0;
825 }
826
827 int getpeersec(int fd, char **ret) {
828 socklen_t n = 64;
829 char *s;
830 int r;
831
832 assert(fd >= 0);
833 assert(ret);
834
835 s = new0(char, n);
836 if (!s)
837 return -ENOMEM;
838
839 r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
840 if (r < 0) {
841 free(s);
842
843 if (errno != ERANGE)
844 return -errno;
845
846 s = new0(char, n);
847 if (!s)
848 return -ENOMEM;
849
850 r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
851 if (r < 0) {
852 free(s);
853 return -errno;
854 }
855 }
856
857 if (isempty(s)) {
858 free(s);
859 return -EOPNOTSUPP;
860 }
861
862 *ret = s;
863 return 0;
864 }
865
866 int send_one_fd(int transport_fd, int fd, int flags) {
867 union {
868 struct cmsghdr cmsghdr;
869 uint8_t buf[CMSG_SPACE(sizeof(int))];
870 } control = {};
871 struct msghdr mh = {
872 .msg_control = &control,
873 .msg_controllen = sizeof(control),
874 };
875 struct cmsghdr *cmsg;
876
877 assert(transport_fd >= 0);
878 assert(fd >= 0);
879
880 cmsg = CMSG_FIRSTHDR(&mh);
881 cmsg->cmsg_level = SOL_SOCKET;
882 cmsg->cmsg_type = SCM_RIGHTS;
883 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
884 memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
885
886 mh.msg_controllen = CMSG_SPACE(sizeof(int));
887 if (sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags) < 0)
888 return -errno;
889
890 return 0;
891 }
892
893 int receive_one_fd(int transport_fd, int flags) {
894 union {
895 struct cmsghdr cmsghdr;
896 uint8_t buf[CMSG_SPACE(sizeof(int))];
897 } control = {};
898 struct msghdr mh = {
899 .msg_control = &control,
900 .msg_controllen = sizeof(control),
901 };
902 struct cmsghdr *cmsg, *found = NULL;
903
904 assert(transport_fd >= 0);
905
906 /*
907 * Receive a single FD via @transport_fd. We don't care for
908 * the transport-type. We retrieve a single FD at most, so for
909 * packet-based transports, the caller must ensure to send
910 * only a single FD per packet. This is best used in
911 * combination with send_one_fd().
912 */
913
914 if (recvmsg(transport_fd, &mh, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC | flags) < 0)
915 return -errno;
916
917 CMSG_FOREACH(cmsg, &mh) {
918 if (cmsg->cmsg_level == SOL_SOCKET &&
919 cmsg->cmsg_type == SCM_RIGHTS &&
920 cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
921 assert(!found);
922 found = cmsg;
923 break;
924 }
925 }
926
927 if (!found) {
928 cmsg_close_all(&mh);
929 return -EIO;
930 }
931
932 return *(int*) CMSG_DATA(found);
933 }