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