Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014--2025 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Affero General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
12 :
13 : You should have received a copy of the GNU Affero General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file mhd_config.c
18 : * @brief functions to configure and setup MHD
19 : * @author Florian Dold
20 : * @author Benedikt Mueller
21 : * @author Christian Grothoff
22 : */
23 : #include "platform.h" /* UNNECESSARY? */
24 : #include <gnunet/gnunet_util_lib.h>
25 : #include "taler/taler_mhd_lib.h"
26 :
27 :
28 : /**
29 : * Backlog for listen operation.
30 : */
31 : #define LISTEN_BACKLOG 500
32 :
33 :
34 : /**
35 : * Function called for logging by MHD.
36 : *
37 : * @param cls closure, NULL
38 : * @param fm format string (`printf()`-style)
39 : * @param ap arguments to @a fm
40 : */
41 : void
42 46 : TALER_MHD_handle_logs (void *cls,
43 : const char *fm,
44 : va_list ap)
45 : {
46 : static int cache;
47 : char buf[2048];
48 :
49 : (void) cls;
50 46 : if (-1 == cache)
51 2 : return;
52 45 : if (0 == cache)
53 : {
54 23 : if (0 ==
55 23 : GNUNET_get_log_call_status (GNUNET_ERROR_TYPE_INFO,
56 : "libmicrohttpd",
57 : __FILE__,
58 : __FUNCTION__,
59 : __LINE__))
60 : {
61 1 : cache = -1;
62 1 : return;
63 : }
64 : }
65 44 : cache = 1;
66 44 : vsnprintf (buf,
67 : sizeof (buf),
68 : fm,
69 : ap);
70 44 : GNUNET_log_from_nocheck (GNUNET_ERROR_TYPE_INFO,
71 : "libmicrohttpd",
72 : "%s",
73 : buf);
74 : }
75 :
76 :
77 : /**
78 : * Open UNIX domain socket for listining at @a unix_path with
79 : * permissions @a unix_mode.
80 : *
81 : * @param unix_path where to listen
82 : * @param unix_mode access permissions to set
83 : * @return -1 on error, otherwise the listen socket
84 : */
85 : static int
86 0 : open_unix_path (const char *unix_path,
87 : mode_t unix_mode)
88 : {
89 : struct GNUNET_NETWORK_Handle *nh;
90 : struct sockaddr_un *un;
91 :
92 0 : if (sizeof (un->sun_path) <= strlen (unix_path))
93 : {
94 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
95 : "unixpath `%s' is too long\n",
96 : unix_path);
97 0 : return -1;
98 : }
99 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
100 : "Creating listen socket '%s' with mode %o\n",
101 : unix_path,
102 : unix_mode);
103 :
104 0 : if (GNUNET_OK !=
105 0 : GNUNET_DISK_directory_create_for_file (unix_path))
106 : {
107 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
108 : "mkdir",
109 : unix_path);
110 : }
111 :
112 0 : un = GNUNET_new (struct sockaddr_un);
113 0 : un->sun_family = AF_UNIX;
114 0 : strncpy (un->sun_path,
115 : unix_path,
116 : sizeof (un->sun_path) - 1);
117 0 : GNUNET_NETWORK_unix_precheck (un);
118 :
119 0 : if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX,
120 : SOCK_STREAM,
121 : 0)))
122 : {
123 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
124 : "socket");
125 0 : GNUNET_free (un);
126 0 : return -1;
127 : }
128 :
129 0 : if (GNUNET_OK !=
130 0 : GNUNET_NETWORK_socket_bind (nh,
131 : (void *) un,
132 : sizeof (struct sockaddr_un)))
133 : {
134 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
135 : "bind",
136 : unix_path);
137 0 : GNUNET_free (un);
138 0 : GNUNET_NETWORK_socket_close (nh);
139 0 : return -1;
140 : }
141 0 : GNUNET_free (un);
142 0 : if (GNUNET_OK !=
143 0 : GNUNET_NETWORK_socket_listen (nh,
144 : LISTEN_BACKLOG))
145 : {
146 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
147 : "listen");
148 0 : GNUNET_NETWORK_socket_close (nh);
149 0 : return -1;
150 : }
151 :
152 0 : if (0 != chmod (unix_path,
153 : unix_mode))
154 : {
155 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
156 : "chmod");
157 0 : GNUNET_NETWORK_socket_close (nh);
158 0 : return -1;
159 : }
160 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
161 : "set socket '%s' to mode %o\n",
162 : unix_path,
163 : unix_mode);
164 :
165 : /* extract and return actual socket handle from 'nh' */
166 : {
167 : int fd;
168 :
169 0 : fd = GNUNET_NETWORK_get_fd (nh);
170 0 : GNUNET_NETWORK_socket_free_memory_only_ (nh);
171 0 : return fd;
172 : }
173 : }
174 :
175 :
176 : enum GNUNET_GenericReturnValue
177 23 : TALER_MHD_listen_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
178 : const char *section,
179 : TALER_MHD_ListenSocketCallback cb,
180 : void *cb_cls)
181 : {
182 23 : const char *choices[] = {
183 : "tcp",
184 : "unix",
185 : "systemd",
186 : NULL
187 : };
188 : const char *serve_type;
189 23 : enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
190 :
191 23 : if (GNUNET_OK !=
192 23 : GNUNET_CONFIGURATION_get_value_choice (cfg,
193 : section,
194 : "SERVE",
195 : choices,
196 : &serve_type))
197 : {
198 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
199 : section,
200 : "SERVE",
201 : "serve type (tcp, unix or systemd) required");
202 0 : return GNUNET_SYSERR;
203 : }
204 :
205 :
206 : /* try systemd passing always first */
207 : {
208 : const char *listen_pid;
209 : const char *listen_fds;
210 : const char *listen_fdn;
211 :
212 : /* check for systemd-style FD passing */
213 23 : if ( (NULL != (listen_pid = getenv ("LISTEN_PID"))) &&
214 0 : (getpid () ==
215 0 : strtol (listen_pid,
216 : NULL,
217 0 : 10)) &&
218 0 : (NULL != (listen_fds = getenv ("LISTEN_FDS"))) &&
219 0 : (NULL != (listen_fdn = getenv ("LISTEN_FDNAMES"))) )
220 : {
221 0 : int off = 3;
222 : unsigned int cnt;
223 : char dummy;
224 :
225 0 : if (0 != strcmp (serve_type,
226 : "systemd"))
227 : {
228 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
229 : "Using systemd activation due to environment variables. Set SERVE=systemd to disable this warning!\n");
230 : }
231 : else
232 : {
233 0 : ret = GNUNET_NO;
234 : }
235 0 : if (1 != sscanf (listen_fds,
236 : "%u%c",
237 : &cnt,
238 : &dummy))
239 : {
240 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
241 : "Invalid number `%s' of systemd sockets specified\n",
242 : listen_fds);
243 : }
244 : else
245 : {
246 : char *fdns;
247 :
248 0 : fdns = GNUNET_strdup (listen_fdn);
249 0 : for (const char *tok = strtok (fdns,
250 : ":");
251 0 : cnt-- > 0;
252 0 : tok = strtok (NULL,
253 : ":"))
254 : {
255 0 : int fh = off++;
256 : int flags;
257 :
258 0 : flags = fcntl (fh,
259 : F_GETFD);
260 0 : if ( (-1 == flags) &&
261 0 : (EBADF == errno) )
262 : {
263 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
264 : "Bad listen socket %s (%d) passed, ignored\n",
265 : tok,
266 : fh);
267 0 : continue;
268 : }
269 0 : flags |= FD_CLOEXEC;
270 0 : if (0 != fcntl (fh,
271 : F_SETFD,
272 : flags))
273 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
274 : "fcntl");
275 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
276 : "Successfully obtained listen socket %s (#%d) from hypervisor\n",
277 : tok,
278 : fh);
279 0 : cb (cb_cls,
280 : fh);
281 0 : ret = GNUNET_OK;
282 : }
283 0 : GNUNET_free (fdns);
284 : }
285 : }
286 : }
287 :
288 : /* now try configuration file */
289 23 : if (0 == strcmp (serve_type,
290 : "unix"))
291 : {
292 : char *serve_unixpath;
293 : mode_t unixpath_mode;
294 : struct sockaddr_un s_un;
295 : char *modestring;
296 :
297 0 : if (GNUNET_OK !=
298 0 : GNUNET_CONFIGURATION_get_value_filename (cfg,
299 : section,
300 : "UNIXPATH",
301 : &serve_unixpath))
302 : {
303 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
304 : section,
305 : "UNIXPATH",
306 : "UNIXPATH value required");
307 0 : return GNUNET_SYSERR;
308 : }
309 0 : if (strlen (serve_unixpath) >= sizeof (s_un.sun_path))
310 : {
311 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
312 : "unixpath `%s' is too long\n",
313 : serve_unixpath);
314 0 : GNUNET_free (serve_unixpath);
315 0 : return GNUNET_SYSERR;
316 : }
317 :
318 0 : if (GNUNET_OK !=
319 0 : GNUNET_CONFIGURATION_get_value_string (cfg,
320 : section,
321 : "UNIXPATH_MODE",
322 : &modestring))
323 : {
324 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
325 : section,
326 : "UNIXPATH_MODE");
327 0 : GNUNET_free (serve_unixpath);
328 0 : return GNUNET_SYSERR;
329 : }
330 : {
331 : char *endp;
332 : unsigned long mode;
333 :
334 0 : errno = 0;
335 0 : mode = strtoul (modestring,
336 : &endp,
337 : 8);
338 : /* strtoul() only sets errno on overflow, so we must
339 : also check that we actually consumed the entire value */
340 0 : if ( (0 != errno) ||
341 0 : (endp == modestring) ||
342 0 : ('\0' != *endp) ||
343 0 : (mode != (unsigned long) (mode_t) mode) )
344 : {
345 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
346 : section,
347 : "UNIXPATH_MODE",
348 : "must be octal number");
349 0 : GNUNET_free (modestring);
350 0 : GNUNET_free (serve_unixpath);
351 0 : return GNUNET_SYSERR;
352 : }
353 0 : unixpath_mode = (mode_t) mode;
354 : }
355 0 : GNUNET_free (modestring);
356 :
357 : {
358 : int fd;
359 :
360 0 : fd = open_unix_path (serve_unixpath,
361 : unixpath_mode);
362 0 : GNUNET_free (serve_unixpath);
363 0 : if (-1 == fd)
364 0 : return GNUNET_NO;
365 0 : cb (cb_cls,
366 : fd);
367 0 : return GNUNET_OK;
368 : }
369 : }
370 :
371 23 : if (0 == strcasecmp (serve_type,
372 : "tcp"))
373 : {
374 : unsigned long long lport;
375 : struct GNUNET_NETWORK_Handle *nh;
376 : char *bind_to;
377 :
378 23 : if (GNUNET_OK !=
379 23 : GNUNET_CONFIGURATION_get_value_number (cfg,
380 : section,
381 : "PORT",
382 : &lport))
383 : {
384 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
385 : section,
386 : "PORT",
387 : "port number required");
388 0 : return GNUNET_SYSERR;
389 : }
390 :
391 23 : if ( (0 == lport) ||
392 23 : (lport > UINT16_MAX) )
393 : {
394 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
395 : section,
396 : "PORT",
397 : "port number not in [1,65535]");
398 0 : return GNUNET_SYSERR;
399 : }
400 :
401 23 : if (GNUNET_OK !=
402 23 : GNUNET_CONFIGURATION_get_value_string (cfg,
403 : section,
404 : "BIND_TO",
405 : &bind_to))
406 23 : bind_to = NULL;
407 :
408 : /* let's have fun binding... */
409 : {
410 : char port_str[6];
411 : struct addrinfo hints;
412 : struct addrinfo *res;
413 : int ec;
414 :
415 23 : GNUNET_snprintf (port_str,
416 : sizeof (port_str),
417 : "%u",
418 : (unsigned int) lport);
419 23 : memset (&hints,
420 : 0,
421 : sizeof (hints));
422 23 : hints.ai_family = AF_UNSPEC;
423 23 : hints.ai_socktype = SOCK_STREAM;
424 23 : hints.ai_protocol = IPPROTO_TCP;
425 23 : hints.ai_flags = AI_PASSIVE
426 : #ifdef AI_IDN
427 : | AI_IDN
428 : #endif
429 : ;
430 :
431 23 : if (0 !=
432 23 : (ec = getaddrinfo (bind_to,
433 : port_str,
434 : &hints,
435 : &res)))
436 : {
437 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
438 : "Failed to resolve BIND_TO address `%s': %s\n",
439 : bind_to,
440 : gai_strerror (ec));
441 0 : GNUNET_free (bind_to);
442 0 : return GNUNET_SYSERR;
443 : }
444 23 : GNUNET_free (bind_to);
445 23 : for (struct addrinfo *ai = res;
446 69 : NULL != ai;
447 46 : ai = ai->ai_next)
448 : {
449 46 : if (NULL == (nh = GNUNET_NETWORK_socket_create (ai->ai_family,
450 : ai->ai_socktype,
451 : ai->ai_protocol)))
452 : {
453 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
454 : "socket");
455 0 : freeaddrinfo (res);
456 0 : return GNUNET_NO;
457 : }
458 : {
459 46 : const int on = 1;
460 :
461 46 : if (GNUNET_OK !=
462 46 : GNUNET_NETWORK_socket_setsockopt (nh,
463 : SOL_SOCKET,
464 : SO_REUSEPORT,
465 : &on,
466 : sizeof(on)))
467 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
468 : "setsockopt");
469 : }
470 46 : if (GNUNET_OK !=
471 46 : GNUNET_NETWORK_socket_bind (nh,
472 46 : ai->ai_addr,
473 : ai->ai_addrlen))
474 : {
475 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
476 : "bind");
477 0 : GNUNET_break (GNUNET_OK ==
478 : GNUNET_NETWORK_socket_close (nh));
479 0 : freeaddrinfo (res);
480 0 : return GNUNET_NO;
481 : }
482 :
483 46 : if (GNUNET_OK !=
484 46 : GNUNET_NETWORK_socket_listen (nh,
485 : LISTEN_BACKLOG))
486 : {
487 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
488 : "listen");
489 0 : GNUNET_SCHEDULER_shutdown ();
490 0 : GNUNET_break (GNUNET_OK ==
491 : GNUNET_NETWORK_socket_close (nh));
492 0 : freeaddrinfo (res);
493 0 : return GNUNET_NO;
494 : }
495 :
496 : /* extract and return actual socket handle from 'nh' */
497 : {
498 : int fh;
499 :
500 46 : fh = GNUNET_NETWORK_get_fd (nh);
501 46 : GNUNET_NETWORK_socket_free_memory_only_ (nh);
502 46 : if (-1 == fh)
503 : {
504 0 : GNUNET_break (0);
505 0 : freeaddrinfo (res);
506 0 : return GNUNET_NO;
507 : }
508 46 : cb (cb_cls,
509 : fh);
510 : }
511 : } /* for all addrinfos */
512 23 : freeaddrinfo (res);
513 23 : return GNUNET_OK;
514 : } /* bind data scope */
515 : } /* tcp */
516 0 : return ret;
517 : }
|