chiark / gitweb /
util: unify SO_PEERCRED/SO_PEERSEC invocations
[elogind.git] / src / shared / socket-label.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <assert.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <arpa/inet.h>
28 #include <stdio.h>
29 #include <net/if.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <stddef.h>
33 #include <sys/ioctl.h>
34
35 #include "macro.h"
36 #include "util.h"
37 #include "mkdir.h"
38 #include "socket-util.h"
39 #include "missing.h"
40 #include "label.h"
41
42 int socket_address_listen(
43                 const SocketAddress *a,
44                 int flags,
45                 int backlog,
46                 SocketAddressBindIPv6Only only,
47                 const char *bind_to_device,
48                 bool free_bind,
49                 bool transparent,
50                 mode_t directory_mode,
51                 mode_t socket_mode,
52                 const char *label) {
53
54         _cleanup_close_ int fd = -1;
55         int r, one;
56
57         assert(a);
58
59         r = socket_address_verify(a);
60         if (r < 0)
61                 return r;
62
63         if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported())
64                 return -EAFNOSUPPORT;
65
66         if (label) {
67                 r = label_socket_set(label);
68                 if (r < 0)
69                         return r;
70         }
71
72         fd = socket(socket_address_family(a), a->type | flags, a->protocol);
73         r = fd < 0 ? -errno : 0;
74
75         if (label)
76                 label_socket_clear();
77
78         if (r < 0)
79                 return r;
80
81         if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
82                 int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
83
84                 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
85                         return -errno;
86         }
87
88         if (socket_address_family(a) == AF_INET || socket_address_family(a) == AF_INET6) {
89                 if (bind_to_device)
90                         if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0)
91                                 return -errno;
92
93                 if (free_bind) {
94                         one = 1;
95                         if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
96                                 log_warning("IP_FREEBIND failed: %m");
97                 }
98
99                 if (transparent) {
100                         one = 1;
101                         if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
102                                 log_warning("IP_TRANSPARENT failed: %m");
103                 }
104         }
105
106         one = 1;
107         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
108                 return -errno;
109
110         if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) {
111                 mode_t old_mask;
112
113                 /* Create parents */
114                 mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode);
115
116                 /* Enforce the right access mode for the socket */
117                 old_mask = umask(~ socket_mode);
118
119                 /* Include the original umask in our mask */
120                 umask(~socket_mode | old_mask);
121
122                 r = label_bind(fd, &a->sockaddr.sa, a->size);
123
124                 if (r < 0 && errno == EADDRINUSE) {
125                         /* Unlink and try again */
126                         unlink(a->sockaddr.un.sun_path);
127                         r = bind(fd, &a->sockaddr.sa, a->size);
128                 }
129
130                 umask(old_mask);
131         } else
132                 r = bind(fd, &a->sockaddr.sa, a->size);
133
134         if (r < 0)
135                 return -errno;
136
137         if (socket_address_can_accept(a))
138                 if (listen(fd, backlog) < 0)
139                         return -errno;
140
141         r = fd;
142         fd = -1;
143
144         return r;
145 }