]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/socket-label.c
Merge pull request #2495 from heftig/master
[thirdparty/systemd.git] / src / basic / socket-label.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 <errno.h>
21 #include <netinet/in.h>
22 #include <stdbool.h>
23 #include <stddef.h>
24 #include <string.h>
25 #include <sys/socket.h>
26 #include <sys/stat.h>
27 #include <sys/un.h>
28 #include <unistd.h>
29
30 #include "alloc-util.h"
31 #include "fd-util.h"
32 #include "log.h"
33 #include "macro.h"
34 #include "missing.h"
35 #include "mkdir.h"
36 #include "selinux-util.h"
37 #include "socket-util.h"
38
39 int socket_address_listen(
40 const SocketAddress *a,
41 int flags,
42 int backlog,
43 SocketAddressBindIPv6Only only,
44 const char *bind_to_device,
45 bool reuse_port,
46 bool free_bind,
47 bool transparent,
48 mode_t directory_mode,
49 mode_t socket_mode,
50 const char *label) {
51
52 _cleanup_close_ int fd = -1;
53 int r, one;
54
55 assert(a);
56
57 r = socket_address_verify(a);
58 if (r < 0)
59 return r;
60
61 if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported())
62 return -EAFNOSUPPORT;
63
64 if (label) {
65 r = mac_selinux_create_socket_prepare(label);
66 if (r < 0)
67 return r;
68 }
69
70 fd = socket(socket_address_family(a), a->type | flags, a->protocol);
71 r = fd < 0 ? -errno : 0;
72
73 if (label)
74 mac_selinux_create_socket_clear();
75
76 if (r < 0)
77 return r;
78
79 if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
80 int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
81
82 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
83 return -errno;
84 }
85
86 if (socket_address_family(a) == AF_INET || socket_address_family(a) == AF_INET6) {
87 if (bind_to_device)
88 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0)
89 return -errno;
90
91 if (reuse_port) {
92 one = 1;
93 if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0)
94 log_warning_errno(errno, "SO_REUSEPORT failed: %m");
95 }
96
97 if (free_bind) {
98 one = 1;
99 if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
100 log_warning_errno(errno, "IP_FREEBIND failed: %m");
101 }
102
103 if (transparent) {
104 one = 1;
105 if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
106 log_warning_errno(errno, "IP_TRANSPARENT failed: %m");
107 }
108 }
109
110 one = 1;
111 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
112 return -errno;
113
114 if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) {
115 mode_t old_mask;
116
117 /* Create parents */
118 mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode);
119
120 /* Enforce the right access mode for the socket */
121 old_mask = umask(~ socket_mode);
122
123 r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
124
125 if (r < 0 && errno == EADDRINUSE) {
126 /* Unlink and try again */
127 unlink(a->sockaddr.un.sun_path);
128 r = bind(fd, &a->sockaddr.sa, a->size);
129 }
130
131 umask(old_mask);
132 } else
133 r = bind(fd, &a->sockaddr.sa, a->size);
134
135 if (r < 0)
136 return -errno;
137
138 if (socket_address_can_accept(a))
139 if (listen(fd, backlog) < 0)
140 return -errno;
141
142 r = fd;
143 fd = -1;
144
145 return r;
146 }
147
148 int make_socket_fd(int log_level, const char* address, int type, int flags) {
149 SocketAddress a;
150 int fd, r;
151
152 r = socket_address_parse(&a, address);
153 if (r < 0)
154 return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address);
155
156 a.type = type;
157
158 fd = socket_address_listen(&a, type | 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 }