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 "taler/platform.h"
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 114 : 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 114 : if (-1 == cache)
51 2 : return;
52 113 : if (0 == cache)
53 : {
54 57 : if (0 ==
55 57 : 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 112 : cache = 1;
66 112 : vsnprintf (buf,
67 : sizeof (buf),
68 : fm,
69 : ap);
70 112 : 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 59 : 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 59 : const char *choices[] = {
183 : "tcp",
184 : "unix",
185 : "systemd",
186 : NULL
187 : };
188 : const char *serve_type;
189 59 : enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
190 :
191 59 : if (GNUNET_OK !=
192 59 : 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 59 : 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 59 : 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 0 : errno = 0;
331 0 : unixpath_mode = (mode_t) strtoul (modestring,
332 : NULL,
333 : 8);
334 0 : if (0 != errno)
335 : {
336 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
337 : section,
338 : "UNIXPATH_MODE",
339 : "must be octal number");
340 0 : GNUNET_free (modestring);
341 0 : GNUNET_free (serve_unixpath);
342 0 : return GNUNET_SYSERR;
343 : }
344 0 : GNUNET_free (modestring);
345 :
346 : {
347 : int fd;
348 :
349 0 : fd = open_unix_path (serve_unixpath,
350 : unixpath_mode);
351 0 : GNUNET_free (serve_unixpath);
352 0 : if (-1 == fd)
353 0 : return GNUNET_NO;
354 0 : cb (cb_cls,
355 : fd);
356 0 : return GNUNET_OK;
357 : }
358 : }
359 :
360 59 : if (0 == strcasecmp (serve_type,
361 : "tcp"))
362 : {
363 : unsigned long long lport;
364 : struct GNUNET_NETWORK_Handle *nh;
365 : char *bind_to;
366 :
367 59 : if (GNUNET_OK !=
368 59 : GNUNET_CONFIGURATION_get_value_number (cfg,
369 : section,
370 : "PORT",
371 : &lport))
372 : {
373 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
374 : section,
375 : "PORT",
376 : "port number required");
377 0 : return GNUNET_SYSERR;
378 : }
379 :
380 59 : if ( (0 == lport) ||
381 59 : (lport > UINT16_MAX) )
382 : {
383 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
384 : section,
385 : "PORT",
386 : "port number not in [1,65535]");
387 0 : return GNUNET_SYSERR;
388 : }
389 :
390 59 : if (GNUNET_OK !=
391 59 : GNUNET_CONFIGURATION_get_value_string (cfg,
392 : section,
393 : "BIND_TO",
394 : &bind_to))
395 59 : bind_to = NULL;
396 :
397 : /* let's have fun binding... */
398 : {
399 : char port_str[6];
400 : struct addrinfo hints;
401 : struct addrinfo *res;
402 : int ec;
403 :
404 59 : GNUNET_snprintf (port_str,
405 : sizeof (port_str),
406 : "%u",
407 : (unsigned int) lport);
408 59 : memset (&hints,
409 : 0,
410 : sizeof (hints));
411 59 : hints.ai_family = AF_UNSPEC;
412 59 : hints.ai_socktype = SOCK_STREAM;
413 59 : hints.ai_protocol = IPPROTO_TCP;
414 59 : hints.ai_flags = AI_PASSIVE
415 : #ifdef AI_IDN
416 : | AI_IDN
417 : #endif
418 : ;
419 :
420 59 : if (0 !=
421 59 : (ec = getaddrinfo (bind_to,
422 : port_str,
423 : &hints,
424 : &res)))
425 : {
426 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
427 : "Failed to resolve BIND_TO address `%s': %s\n",
428 : bind_to,
429 : gai_strerror (ec));
430 0 : GNUNET_free (bind_to);
431 0 : return GNUNET_SYSERR;
432 : }
433 59 : GNUNET_free (bind_to);
434 59 : for (struct addrinfo *ai = res;
435 177 : NULL != ai;
436 118 : ai = ai->ai_next)
437 : {
438 118 : if (NULL == (nh = GNUNET_NETWORK_socket_create (ai->ai_family,
439 : ai->ai_socktype,
440 : ai->ai_protocol)))
441 : {
442 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
443 : "socket");
444 0 : freeaddrinfo (res);
445 0 : return GNUNET_NO;
446 : }
447 : {
448 118 : const int on = 1;
449 :
450 118 : if (GNUNET_OK !=
451 118 : GNUNET_NETWORK_socket_setsockopt (nh,
452 : SOL_SOCKET,
453 : SO_REUSEPORT,
454 : &on,
455 : sizeof(on)))
456 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
457 : "setsockopt");
458 : }
459 118 : if (GNUNET_OK !=
460 118 : GNUNET_NETWORK_socket_bind (nh,
461 118 : ai->ai_addr,
462 : ai->ai_addrlen))
463 : {
464 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
465 : "bind");
466 0 : GNUNET_break (GNUNET_OK ==
467 : GNUNET_NETWORK_socket_close (nh));
468 0 : freeaddrinfo (res);
469 0 : return GNUNET_NO;
470 : }
471 :
472 118 : if (GNUNET_OK !=
473 118 : GNUNET_NETWORK_socket_listen (nh,
474 : LISTEN_BACKLOG))
475 : {
476 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
477 : "listen");
478 0 : GNUNET_SCHEDULER_shutdown ();
479 0 : GNUNET_break (GNUNET_OK ==
480 : GNUNET_NETWORK_socket_close (nh));
481 0 : freeaddrinfo (res);
482 0 : return GNUNET_NO;
483 : }
484 :
485 : /* extract and return actual socket handle from 'nh' */
486 : {
487 : int fh;
488 :
489 118 : fh = GNUNET_NETWORK_get_fd (nh);
490 118 : GNUNET_NETWORK_socket_free_memory_only_ (nh);
491 118 : if (-1 == fh)
492 : {
493 0 : GNUNET_break (0);
494 0 : return GNUNET_NO;
495 : }
496 118 : cb (cb_cls,
497 : fh);
498 : }
499 : } /* for all addrinfos */
500 59 : freeaddrinfo (res);
501 59 : return GNUNET_OK;
502 : } /* bind data scope */
503 : } /* tcp */
504 0 : return ret;
505 : }
|