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 : }
|