Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <errno.h>
4 : : #include <netinet/in.h>
5 : : #include <stdbool.h>
6 : : #include <stddef.h>
7 : : #include <string.h>
8 : : #include <sys/socket.h>
9 : : #include <sys/un.h>
10 : : #include <unistd.h>
11 : :
12 : : #include "alloc-util.h"
13 : : #include "fd-util.h"
14 : : #include "fs-util.h"
15 : : #include "log.h"
16 : : #include "macro.h"
17 : : #include "missing.h"
18 : : #include "mkdir.h"
19 : : #include "selinux-util.h"
20 : : #include "socket-util.h"
21 : : #include "umask-util.h"
22 : :
23 : 0 : int socket_address_listen(
24 : : const SocketAddress *a,
25 : : int flags,
26 : : int backlog,
27 : : SocketAddressBindIPv6Only only,
28 : : const char *bind_to_device,
29 : : bool reuse_port,
30 : : bool free_bind,
31 : : bool transparent,
32 : : mode_t directory_mode,
33 : : mode_t socket_mode,
34 : : const char *label) {
35 : :
36 : 0 : _cleanup_close_ int fd = -1;
37 : : const char *p;
38 : : int r;
39 : :
40 [ # # ]: 0 : assert(a);
41 : :
42 : 0 : r = socket_address_verify(a, true);
43 [ # # ]: 0 : if (r < 0)
44 : 0 : return r;
45 : :
46 [ # # # # ]: 0 : if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported())
47 : 0 : return -EAFNOSUPPORT;
48 : :
49 [ # # ]: 0 : if (label) {
50 : 0 : r = mac_selinux_create_socket_prepare(label);
51 [ # # ]: 0 : if (r < 0)
52 : 0 : return r;
53 : : }
54 : :
55 : 0 : fd = socket(socket_address_family(a), a->type | flags, a->protocol);
56 [ # # ]: 0 : r = fd < 0 ? -errno : 0;
57 : :
58 [ # # ]: 0 : if (label)
59 : 0 : mac_selinux_create_socket_clear();
60 : :
61 [ # # ]: 0 : if (r < 0)
62 : 0 : return r;
63 : :
64 [ # # # # ]: 0 : if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
65 : 0 : r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_V6ONLY, only == SOCKET_ADDRESS_IPV6_ONLY);
66 [ # # ]: 0 : if (r < 0)
67 : 0 : return r;
68 : : }
69 : :
70 [ # # # # ]: 0 : if (IN_SET(socket_address_family(a), AF_INET, AF_INET6)) {
71 [ # # ]: 0 : if (bind_to_device) {
72 : 0 : r = socket_bind_to_ifname(fd, bind_to_device);
73 [ # # ]: 0 : if (r < 0)
74 : 0 : return r;
75 : : }
76 : :
77 [ # # ]: 0 : if (reuse_port) {
78 : 0 : r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEPORT, true);
79 [ # # ]: 0 : if (r < 0)
80 [ # # ]: 0 : log_warning_errno(r, "SO_REUSEPORT failed: %m");
81 : : }
82 : :
83 [ # # ]: 0 : if (free_bind) {
84 : 0 : r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true);
85 [ # # ]: 0 : if (r < 0)
86 [ # # ]: 0 : log_warning_errno(r, "IP_FREEBIND failed: %m");
87 : : }
88 : :
89 [ # # ]: 0 : if (transparent) {
90 : 0 : r = setsockopt_int(fd, IPPROTO_IP, IP_TRANSPARENT, true);
91 [ # # ]: 0 : if (r < 0)
92 [ # # ]: 0 : log_warning_errno(r, "IP_TRANSPARENT failed: %m");
93 : : }
94 : : }
95 : :
96 : 0 : r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
97 [ # # ]: 0 : if (r < 0)
98 : 0 : return r;
99 : :
100 : 0 : p = socket_address_get_path(a);
101 [ # # ]: 0 : if (p) {
102 : : /* Create parents */
103 : 0 : (void) mkdir_parents_label(p, directory_mode);
104 : :
105 : : /* Enforce the right access mode for the socket */
106 [ # # # # ]: 0 : RUN_WITH_UMASK(~socket_mode) {
107 : 0 : r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
108 [ # # ]: 0 : if (r == -EADDRINUSE) {
109 : : /* Unlink and try again */
110 : :
111 [ # # ]: 0 : if (unlink(p) < 0)
112 : 0 : return r; /* didn't work, return original error */
113 : :
114 : 0 : r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
115 : : }
116 [ # # ]: 0 : if (r < 0)
117 : 0 : return r;
118 : : }
119 : : } else {
120 [ # # ]: 0 : if (bind(fd, &a->sockaddr.sa, a->size) < 0)
121 : 0 : return -errno;
122 : : }
123 : :
124 [ # # ]: 0 : if (socket_address_can_accept(a))
125 [ # # ]: 0 : if (listen(fd, backlog) < 0)
126 : 0 : return -errno;
127 : :
128 : : /* Let's trigger an inotify event on the socket node, so that anyone waiting for this socket to be connectable
129 : : * gets notified */
130 [ # # ]: 0 : if (p)
131 : 0 : (void) touch(p);
132 : :
133 : 0 : r = fd;
134 : 0 : fd = -1;
135 : :
136 : 0 : return r;
137 : : }
138 : :
139 : 0 : int make_socket_fd(int log_level, const char* address, int type, int flags) {
140 : : SocketAddress a;
141 : : int fd, r;
142 : :
143 : 0 : r = socket_address_parse(&a, address);
144 [ # # ]: 0 : if (r < 0)
145 [ # # ]: 0 : return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address);
146 : :
147 : 0 : a.type = type;
148 : :
149 : 0 : fd = socket_address_listen(&a, type | flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
150 : : NULL, false, false, false, 0755, 0644, NULL);
151 [ # # # # ]: 0 : if (fd < 0 || log_get_max_level() >= log_level) {
152 [ # # ]: 0 : _cleanup_free_ char *p = NULL;
153 : :
154 : 0 : r = socket_address_print(&a, &p);
155 [ # # ]: 0 : if (r < 0)
156 [ # # ]: 0 : return log_error_errno(r, "socket_address_print(): %m");
157 : :
158 [ # # ]: 0 : if (fd < 0)
159 [ # # ]: 0 : log_error_errno(fd, "Failed to listen on %s: %m", p);
160 : : else
161 [ # # ]: 0 : log_full(log_level, "Listening on %s", p);
162 : : }
163 : :
164 : 0 : return fd;
165 : : }
|