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