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