]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/socket-label.c
Merge pull request #2056 from evverx/expose-soft-limits-on-the-bus
[thirdparty/systemd.git] / src / basic / 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 <errno.h>
23 #include <netinet/in.h>
24 #include <stdbool.h>
25 #include <stddef.h>
26 #include <string.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <sys/un.h>
30 #include <unistd.h>
31
32 #include "alloc-util.h"
33 #include "fd-util.h"
34 #include "log.h"
35 #include "macro.h"
36 #include "missing.h"
37 #include "mkdir.h"
38 #include "selinux-util.h"
39 #include "socket-util.h"
40
41 int socket_address_listen(
42 const SocketAddress *a,
43 int flags,
44 int backlog,
45 SocketAddressBindIPv6Only only,
46 const char *bind_to_device,
47 bool reuse_port,
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 = mac_selinux_create_socket_prepare(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 mac_selinux_create_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 (reuse_port) {
94 one = 1;
95 if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0)
96 log_warning_errno(errno, "SO_REUSEPORT failed: %m");
97 }
98
99 if (free_bind) {
100 one = 1;
101 if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
102 log_warning_errno(errno, "IP_FREEBIND failed: %m");
103 }
104
105 if (transparent) {
106 one = 1;
107 if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
108 log_warning_errno(errno, "IP_TRANSPARENT failed: %m");
109 }
110 }
111
112 one = 1;
113 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
114 return -errno;
115
116 if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) {
117 mode_t old_mask;
118
119 /* Create parents */
120 mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode);
121
122 /* Enforce the right access mode for the socket */
123 old_mask = umask(~ socket_mode);
124
125 r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
126
127 if (r < 0 && errno == EADDRINUSE) {
128 /* Unlink and try again */
129 unlink(a->sockaddr.un.sun_path);
130 r = bind(fd, &a->sockaddr.sa, a->size);
131 }
132
133 umask(old_mask);
134 } else
135 r = bind(fd, &a->sockaddr.sa, a->size);
136
137 if (r < 0)
138 return -errno;
139
140 if (socket_address_can_accept(a))
141 if (listen(fd, backlog) < 0)
142 return -errno;
143
144 r = fd;
145 fd = -1;
146
147 return r;
148 }
149
150 int make_socket_fd(int log_level, const char* address, int flags) {
151 SocketAddress a;
152 int fd, r;
153
154 r = socket_address_parse(&a, address);
155 if (r < 0)
156 return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address);
157
158 fd = socket_address_listen(&a, flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
159 NULL, false, false, false, 0755, 0644, NULL);
160 if (fd < 0 || log_get_max_level() >= log_level) {
161 _cleanup_free_ char *p = NULL;
162
163 r = socket_address_print(&a, &p);
164 if (r < 0)
165 return log_error_errno(r, "socket_address_print(): %m");
166
167 if (fd < 0)
168 log_error_errno(fd, "Failed to listen on %s: %m", p);
169 else
170 log_full(log_level, "Listening on %s", p);
171 }
172
173 return fd;
174 }