Line data Source code
1 : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2 :
3 : /***
4 : This file is based on sd-daemon.c from systemd. Unused code has been
5 : removed.
6 :
7 : Copyright 2010 Lennart Poettering
8 :
9 : systemd is free software; you can redistribute it and/or modify it
10 : under the terms of the GNU Lesser General Public License as published by
11 : the Free Software Foundation; either version 2.1 of the License, or
12 : (at your option) any later version.
13 :
14 : systemd is distributed in the hope that it will be useful, but
15 : WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : Lesser General Public License for more details.
18 :
19 : You should have received a copy of the GNU Lesser General Public License
20 : along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 : ***/
22 :
23 : #define _GNU_SOURCE
24 : #include <assert.h>
25 : #include <errno.h>
26 : #include <fcntl.h>
27 : #include <limits.h>
28 : #include <netinet/in.h>
29 : #include <stdlib.h>
30 : #include <sys/resource.h>
31 : #include <sys/socket.h>
32 : #include <sys/stat.h>
33 : #include <sys/types.h>
34 : #include <unistd.h>
35 :
36 : #include "sd-daemon.h"
37 : #include "lwan-config.h"
38 :
39 0 : static void unsetenv_listen_vars(void) {
40 0 : unsetenv("LISTEN_PID");
41 0 : unsetenv("LISTEN_FDS");
42 0 : unsetenv("LISTEN_FDNAMES");
43 0 : }
44 :
45 0 : int sd_listen_fds(int unset_environment) {
46 : int n, l, r, fd;
47 : const char *e;
48 :
49 0 : e = getenv("LISTEN_PID");
50 0 : if (!e) {
51 0 : r = 0;
52 0 : goto finish;
53 : }
54 :
55 0 : l = parse_int(e, -1);
56 0 : if (l <= 0) {
57 0 : r = -EINVAL;
58 0 : goto finish;
59 : }
60 :
61 : /* Is this for us? */
62 0 : if (getpid() != (pid_t)l) {
63 0 : r = 0;
64 0 : goto finish;
65 : }
66 :
67 0 : e = getenv("LISTEN_FDS");
68 0 : if (!e) {
69 0 : r = 0;
70 0 : goto finish;
71 : }
72 :
73 0 : n = parse_int(e, -1);
74 0 : if (!n) {
75 0 : r = 0;
76 0 : goto finish;
77 : }
78 :
79 : static_assert(SD_LISTEN_FDS_START < INT_MAX, "");
80 0 : if (n < 0 || n > INT_MAX - SD_LISTEN_FDS_START) {
81 0 : r = -EINVAL;
82 0 : goto finish;
83 : }
84 :
85 0 : for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int)n; fd++) {
86 : int flags;
87 :
88 0 : flags = fcntl(fd, F_GETFD);
89 0 : if (flags < 0) {
90 0 : r = -errno;
91 0 : goto finish;
92 : }
93 :
94 0 : if (flags & FD_CLOEXEC)
95 0 : continue;
96 :
97 0 : if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
98 0 : r = -errno;
99 0 : goto finish;
100 : }
101 : }
102 :
103 0 : r = n;
104 :
105 0 : finish:
106 0 : if (unset_environment)
107 0 : unsetenv_listen_vars();
108 :
109 0 : return r;
110 : }
111 :
112 : /* Both strv_extend_n() and strv_split() aren't simple copies of the
113 : * same functions from systemd. These are simplified versions of those
114 : * functions, used only in the sd_listen_fds_with_names() ported from
115 : * newer versions of systemd.
116 : */
117 0 : static int strv_extend_n(char ***p, const char *s, int n) {
118 0 : if (!p)
119 0 : return -EINVAL;
120 0 : if (!s)
121 0 : return -EINVAL;
122 :
123 0 : *p = calloc((size_t)n, sizeof(char *));
124 0 : if (!p)
125 0 : return -ENOMEM;
126 :
127 0 : size_t s_size = strlen(s) + 1;
128 0 : char *copies = calloc((size_t)(n + 1), s_size);
129 0 : if (!copies) {
130 0 : free(*p);
131 0 : return -ENOMEM;
132 : }
133 0 : for (int i = 0; i < n; i++) {
134 0 : char *copy = &copies[(size_t)i * s_size];
135 0 : *p[i] = memcpy(copy, s, s_size);
136 : }
137 :
138 0 : return 0;
139 : }
140 :
141 0 : static int strv_split(char ***p, const char *value, const char separator) {
142 0 : char *copy = strdup(value);
143 0 : int n_split = 0;
144 :
145 0 : if (!copy)
146 0 : return -ENOMEM;
147 :
148 0 : for (char *c = copy; *c; ) {
149 0 : char *sep_pos = strchr(c, separator);
150 0 : if (!sep_pos)
151 0 : break;
152 :
153 0 : n_split++;
154 0 : c = sep_pos + 1;
155 : }
156 :
157 0 : if (!n_split)
158 0 : return 0;
159 :
160 0 : *p = calloc((size_t)(n_split + 1), sizeof(char *));
161 0 : if (!*p) {
162 0 : free(copy);
163 0 : return -ENOMEM;
164 : }
165 :
166 0 : int i = 0;
167 0 : for (char *c = copy; *c; ) {
168 0 : char *sep_pos = strchr(c, separator);
169 0 : if (!sep_pos)
170 0 : break;
171 :
172 0 : *sep_pos = '\0';
173 0 : *p[i++] = c;
174 0 : c = sep_pos + 1;
175 : }
176 :
177 0 : return n_split;
178 : }
179 :
180 0 : int sd_listen_fds_with_names(int unset_environment, char ***names) {
181 0 : char **l = NULL;
182 : bool have_names;
183 0 : int n_names = 0, n_fds;
184 : const char *e;
185 : int r;
186 :
187 0 : if (!names)
188 0 : return sd_listen_fds(unset_environment);
189 :
190 0 : e = getenv("LISTEN_FDNAMES");
191 0 : if (e) {
192 0 : n_names = strv_split(&l, e, ':');
193 0 : if (n_names < 0) {
194 0 : if (unset_environment)
195 0 : unsetenv_listen_vars();
196 0 : return n_names;
197 : }
198 :
199 0 : have_names = true;
200 : } else {
201 0 : have_names = false;
202 : }
203 :
204 0 : n_fds = sd_listen_fds(unset_environment);
205 0 : if (n_fds <= 0) {
206 0 : r = n_fds;
207 0 : goto fail;
208 : }
209 :
210 0 : if (have_names) {
211 0 : if (n_names != n_fds) {
212 0 : r = -EINVAL;
213 0 : goto fail;
214 : }
215 : } else {
216 0 : r = strv_extend_n(&l, "unknown", n_fds);
217 0 : if (r < 0)
218 0 : goto fail;
219 : }
220 :
221 0 : *names = l;
222 :
223 0 : return n_fds;
224 :
225 0 : fail:
226 0 : free(l);
227 0 : return r;
228 : }
229 :
230 0 : static int sd_is_socket_internal(int fd, int type, int listening) {
231 : struct stat st_fd;
232 :
233 0 : if (fd < 0)
234 0 : return -EBADF;
235 :
236 0 : if (type < 0)
237 0 : return -EINVAL;
238 :
239 0 : if (fstat(fd, &st_fd) < 0)
240 0 : return -errno;
241 :
242 0 : if (!S_ISSOCK(st_fd.st_mode))
243 0 : return 0;
244 :
245 0 : if (type != 0) {
246 0 : int other_type = 0;
247 0 : socklen_t l = sizeof(other_type);
248 :
249 0 : if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
250 0 : return -errno;
251 :
252 0 : if (l != sizeof(other_type))
253 0 : return -EINVAL;
254 :
255 0 : if (other_type != type)
256 0 : return 0;
257 : }
258 :
259 0 : if (listening >= 0) {
260 0 : int accepting = 0;
261 0 : socklen_t l = sizeof(accepting);
262 :
263 0 : if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
264 0 : return -errno;
265 :
266 0 : if (l != sizeof(accepting))
267 0 : return -EINVAL;
268 :
269 0 : if (!accepting != !listening)
270 0 : return 0;
271 : }
272 :
273 0 : return 1;
274 : }
275 :
276 : union sockaddr_union {
277 : struct sockaddr sa;
278 : struct sockaddr_in in4;
279 : struct sockaddr_in6 in6;
280 : };
281 :
282 0 : int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
283 0 : union sockaddr_union sockaddr = {};
284 0 : socklen_t l = sizeof(sockaddr);
285 : int r;
286 :
287 0 : if (family != 0 && family != AF_INET && family != AF_INET6)
288 0 : return -EINVAL;
289 :
290 0 : r = sd_is_socket_internal(fd, type, listening);
291 0 : if (r <= 0)
292 0 : return r;
293 :
294 0 : if (getsockname(fd, &sockaddr.sa, &l) < 0)
295 0 : return -errno;
296 :
297 0 : if (l < sizeof(sa_family_t))
298 0 : return -EINVAL;
299 :
300 0 : if (sockaddr.sa.sa_family != AF_INET &&
301 0 : sockaddr.sa.sa_family != AF_INET6)
302 0 : return 0;
303 :
304 0 : if (family > 0)
305 0 : if (sockaddr.sa.sa_family != family)
306 0 : return 0;
307 :
308 0 : if (port > 0) {
309 0 : if (sockaddr.sa.sa_family == AF_INET) {
310 0 : if (l < sizeof(struct sockaddr_in))
311 0 : return -EINVAL;
312 :
313 0 : return htons(port) == sockaddr.in4.sin_port;
314 : } else {
315 0 : if (l < sizeof(struct sockaddr_in6))
316 0 : return -EINVAL;
317 :
318 0 : return htons(port) == sockaddr.in6.sin6_port;
319 : }
320 : }
321 :
322 0 : return 1;
323 : }
|