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