]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/socket-util.c
Merge pull request #2133 from poettering/import-drop-dkr
[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 <limits.h>
25 #include <net/if.h>
26 #include <netdb.h>
27 #include <netinet/ip.h>
28 #include <stddef.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 #include "alloc-util.h"
36 #include "fd-util.h"
37 #include "fileio.h"
38 #include "formats-util.h"
39 #include "log.h"
40 #include "macro.h"
41 #include "missing.h"
42 #include "parse-util.h"
43 #include "path-util.h"
44 #include "socket-util.h"
45 #include "string-table.h"
46 #include "string-util.h"
47 #include "user-util.h"
48 #include "util.h"
49
50 int socket_address_parse(SocketAddress *a, const char *s) {
51 char *e, *n;
52 unsigned u;
53 int r;
54
55 assert(a);
56 assert(s);
57
58 zero(*a);
59 a->type = SOCK_STREAM;
60
61 if (*s == '[') {
62 /* IPv6 in [x:.....:z]:p notation */
63
64 e = strchr(s+1, ']');
65 if (!e)
66 return -EINVAL;
67
68 n = strndupa(s+1, e-s-1);
69
70 errno = 0;
71 if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
72 return errno > 0 ? -errno : -EINVAL;
73
74 e++;
75 if (*e != ':')
76 return -EINVAL;
77
78 e++;
79 r = safe_atou(e, &u);
80 if (r < 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 e = strchr(s, ':');
117 if (e) {
118 r = safe_atou(e+1, &u);
119 if (r < 0)
120 return r;
121
122 if (u <= 0 || u > 0xFFFF)
123 return -EINVAL;
124
125 n = strndupa(s, e-s);
126
127 /* IPv4 in w.x.y.z:p notation? */
128 r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
129 if (r < 0)
130 return -errno;
131
132 if (r > 0) {
133 /* Gotcha, it's a traditional IPv4 address */
134 a->sockaddr.in.sin_family = AF_INET;
135 a->sockaddr.in.sin_port = htons((uint16_t) u);
136 a->size = sizeof(struct sockaddr_in);
137 } else {
138 unsigned idx;
139
140 if (strlen(n) > IF_NAMESIZE-1)
141 return -EINVAL;
142
143 /* Uh, our last resort, an interface name */
144 idx = if_nametoindex(n);
145 if (idx == 0)
146 return -EINVAL;
147
148 a->sockaddr.in6.sin6_family = AF_INET6;
149 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
150 a->sockaddr.in6.sin6_scope_id = idx;
151 a->sockaddr.in6.sin6_addr = in6addr_any;
152 a->size = sizeof(struct sockaddr_in6);
153 }
154 } else {
155
156 /* Just a port */
157 r = safe_atou(s, &u);
158 if (r < 0)
159 return r;
160
161 if (u <= 0 || u > 0xFFFF)
162 return -EINVAL;
163
164 if (socket_ipv6_is_supported()) {
165 a->sockaddr.in6.sin6_family = AF_INET6;
166 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
167 a->sockaddr.in6.sin6_addr = in6addr_any;
168 a->size = sizeof(struct sockaddr_in6);
169 } else {
170 a->sockaddr.in.sin_family = AF_INET;
171 a->sockaddr.in.sin_port = htons((uint16_t) u);
172 a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
173 a->size = sizeof(struct sockaddr_in);
174 }
175 }
176 }
177
178 return 0;
179 }
180
181 int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
182 SocketAddress b;
183 int r;
184
185 /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
186
187 r = socket_address_parse(&b, s);
188 if (r < 0)
189 return r;
190
191 if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) {
192 log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
193 return -EAFNOSUPPORT;
194 }
195
196 *a = b;
197 return 0;
198 }
199
200 int socket_address_parse_netlink(SocketAddress *a, const char *s) {
201 int family;
202 unsigned group = 0;
203 _cleanup_free_ char *sfamily = NULL;
204 assert(a);
205 assert(s);
206
207 zero(*a);
208 a->type = SOCK_RAW;
209
210 errno = 0;
211 if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
212 return errno > 0 ? -errno : -EINVAL;
213
214 family = netlink_family_from_string(sfamily);
215 if (family < 0)
216 return -EINVAL;
217
218 a->sockaddr.nl.nl_family = AF_NETLINK;
219 a->sockaddr.nl.nl_groups = group;
220
221 a->type = SOCK_RAW;
222 a->size = sizeof(struct sockaddr_nl);
223 a->protocol = family;
224
225 return 0;
226 }
227
228 int socket_address_verify(const SocketAddress *a) {
229 assert(a);
230
231 switch (socket_address_family(a)) {
232
233 case AF_INET:
234 if (a->size != sizeof(struct sockaddr_in))
235 return -EINVAL;
236
237 if (a->sockaddr.in.sin_port == 0)
238 return -EINVAL;
239
240 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
241 return -EINVAL;
242
243 return 0;
244
245 case AF_INET6:
246 if (a->size != sizeof(struct sockaddr_in6))
247 return -EINVAL;
248
249 if (a->sockaddr.in6.sin6_port == 0)
250 return -EINVAL;
251
252 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
253 return -EINVAL;
254
255 return 0;
256
257 case AF_UNIX:
258 if (a->size < offsetof(struct sockaddr_un, sun_path))
259 return -EINVAL;
260
261 if (a->size > offsetof(struct sockaddr_un, sun_path)) {
262
263 if (a->sockaddr.un.sun_path[0] != 0) {
264 char *e;
265
266 /* path */
267 e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
268 if (!e)
269 return -EINVAL;
270
271 if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
272 return -EINVAL;
273 }
274 }
275
276 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
277 return -EINVAL;
278
279 return 0;
280
281 case AF_NETLINK:
282
283 if (a->size != sizeof(struct sockaddr_nl))
284 return -EINVAL;
285
286 if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
287 return -EINVAL;
288
289 return 0;
290
291 default:
292 return -EAFNOSUPPORT;
293 }
294 }
295
296 int socket_address_print(const SocketAddress *a, char **ret) {
297 int r;
298
299 assert(a);
300 assert(ret);
301
302 r = socket_address_verify(a);
303 if (r < 0)
304 return r;
305
306 if (socket_address_family(a) == AF_NETLINK) {
307 _cleanup_free_ char *sfamily = NULL;
308
309 r = netlink_family_to_string_alloc(a->protocol, &sfamily);
310 if (r < 0)
311 return r;
312
313 r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
314 if (r < 0)
315 return -ENOMEM;
316
317 return 0;
318 }
319
320 return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret);
321 }
322
323 bool socket_address_can_accept(const SocketAddress *a) {
324 assert(a);
325
326 return
327 a->type == SOCK_STREAM ||
328 a->type == SOCK_SEQPACKET;
329 }
330
331 bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
332 assert(a);
333 assert(b);
334
335 /* Invalid addresses are unequal to all */
336 if (socket_address_verify(a) < 0 ||
337 socket_address_verify(b) < 0)
338 return false;
339
340 if (a->type != b->type)
341 return false;
342
343 if (socket_address_family(a) != socket_address_family(b))
344 return false;
345
346 switch (socket_address_family(a)) {
347
348 case AF_INET:
349 if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr)
350 return false;
351
352 if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port)
353 return false;
354
355 break;
356
357 case AF_INET6:
358 if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
359 return false;
360
361 if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
362 return false;
363
364 break;
365
366 case AF_UNIX:
367 if (a->size <= offsetof(struct sockaddr_un, sun_path) ||
368 b->size <= offsetof(struct sockaddr_un, sun_path))
369 return false;
370
371 if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
372 return false;
373
374 if (a->sockaddr.un.sun_path[0]) {
375 if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path))
376 return false;
377 } else {
378 if (a->size != b->size)
379 return false;
380
381 if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
382 return false;
383 }
384
385 break;
386
387 case AF_NETLINK:
388 if (a->protocol != b->protocol)
389 return false;
390
391 if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
392 return false;
393
394 break;
395
396 default:
397 /* Cannot compare, so we assume the addresses are different */
398 return false;
399 }
400
401 return true;
402 }
403
404 bool socket_address_is(const SocketAddress *a, const char *s, int type) {
405 struct SocketAddress b;
406
407 assert(a);
408 assert(s);
409
410 if (socket_address_parse(&b, s) < 0)
411 return false;
412
413 b.type = type;
414
415 return socket_address_equal(a, &b);
416 }
417
418 bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
419 struct SocketAddress b;
420
421 assert(a);
422 assert(s);
423
424 if (socket_address_parse_netlink(&b, s) < 0)
425 return false;
426
427 return socket_address_equal(a, &b);
428 }
429
430 const char* socket_address_get_path(const SocketAddress *a) {
431 assert(a);
432
433 if (socket_address_family(a) != AF_UNIX)
434 return NULL;
435
436 if (a->sockaddr.un.sun_path[0] == 0)
437 return NULL;
438
439 return a->sockaddr.un.sun_path;
440 }
441
442 bool socket_ipv6_is_supported(void) {
443 if (access("/proc/net/sockstat6", F_OK) != 0)
444 return false;
445
446 return true;
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_sa(
867 int transport_fd,
868 int fd,
869 const struct sockaddr *sa, socklen_t len,
870 int flags) {
871
872 union {
873 struct cmsghdr cmsghdr;
874 uint8_t buf[CMSG_SPACE(sizeof(int))];
875 } control = {};
876 struct cmsghdr *cmsg;
877
878 struct msghdr mh = {
879 .msg_name = (struct sockaddr*) sa,
880 .msg_namelen = len,
881 .msg_control = &control,
882 .msg_controllen = sizeof(control),
883 };
884
885 assert(transport_fd >= 0);
886 assert(fd >= 0);
887
888 cmsg = CMSG_FIRSTHDR(&mh);
889 cmsg->cmsg_level = SOL_SOCKET;
890 cmsg->cmsg_type = SCM_RIGHTS;
891 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
892 memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
893
894 mh.msg_controllen = CMSG_SPACE(sizeof(int));
895 if (sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags) < 0)
896 return -errno;
897
898 return 0;
899 }
900
901 int receive_one_fd(int transport_fd, int flags) {
902 union {
903 struct cmsghdr cmsghdr;
904 uint8_t buf[CMSG_SPACE(sizeof(int))];
905 } control = {};
906 struct msghdr mh = {
907 .msg_control = &control,
908 .msg_controllen = sizeof(control),
909 };
910 struct cmsghdr *cmsg, *found = NULL;
911
912 assert(transport_fd >= 0);
913
914 /*
915 * Receive a single FD via @transport_fd. We don't care for
916 * the transport-type. We retrieve a single FD at most, so for
917 * packet-based transports, the caller must ensure to send
918 * only a single FD per packet. This is best used in
919 * combination with send_one_fd().
920 */
921
922 if (recvmsg(transport_fd, &mh, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC | flags) < 0)
923 return -errno;
924
925 CMSG_FOREACH(cmsg, &mh) {
926 if (cmsg->cmsg_level == SOL_SOCKET &&
927 cmsg->cmsg_type == SCM_RIGHTS &&
928 cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
929 assert(!found);
930 found = cmsg;
931 break;
932 }
933 }
934
935 if (!found) {
936 cmsg_close_all(&mh);
937 return -EIO;
938 }
939
940 return *(int*) CMSG_DATA(found);
941 }