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