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 57 : 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 57 : const char *choices[] = {
183 : "tcp",
184 : "unix",
185 : "systemd",
186 : NULL
187 : };
188 : const char *serve_type;
189 57 : enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
190 :
191 57 : if (GNUNET_OK !=
192 57 : 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 57 : 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 *fdns;
224 : char dummy;
225 :
226 0 : if (0 != strcmp (serve_type,
227 : "systemd"))
228 : {
229 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
230 : "Using systemd activation due to environment variables. Set SERVE=systemd to disable this warning!\n");
231 : }
232 : else
233 : {
234 0 : ret = GNUNET_NO;
235 : }
236 0 : if (1 != sscanf (listen_fds,
237 : "%u%c",
238 : &cnt,
239 : &dummy))
240 : {
241 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
242 : "Invalid number `%s' of systemd sockets specified\n",
243 : listen_fds);
244 : }
245 : else
246 : {
247 0 : fdns = GNUNET_strdup (listen_fdn);
248 0 : for (const char *tok = strtok (fdns,
249 : ":");
250 0 : cnt-- > 0;
251 0 : tok = strtok (NULL,
252 : ":"))
253 : {
254 0 : int fh = off++;
255 : int flags;
256 :
257 0 : flags = fcntl (fh,
258 : F_GETFD);
259 0 : if ( (-1 == flags) &&
260 0 : (EBADF == errno) )
261 : {
262 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
263 : "Bad listen socket %s (%d) passed, ignored\n",
264 : tok,
265 : fh);
266 0 : continue;
267 : }
268 0 : flags |= FD_CLOEXEC;
269 0 : if (0 != fcntl (fh,
270 : F_SETFD,
271 : flags))
272 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
273 : "fcntl");
274 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
275 : "Successfully obtained listen socket %s (#%d) from hypervisor\n",
276 : tok,
277 : fh);
278 0 : cb (cb_cls,
279 : fh);
280 0 : ret = GNUNET_OK;
281 : }
282 : }
283 0 : GNUNET_free (fdns);
284 : }
285 : }
286 :
287 : /* now try configuration file */
288 57 : if (0 == strcmp (serve_type,
289 : "unix"))
290 : {
291 : char *serve_unixpath;
292 : mode_t unixpath_mode;
293 : struct sockaddr_un s_un;
294 : char *modestring;
295 :
296 0 : if (GNUNET_OK !=
297 0 : GNUNET_CONFIGURATION_get_value_filename (cfg,
298 : section,
299 : "UNIXPATH",
300 : &serve_unixpath))
301 : {
302 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
303 : section,
304 : "UNIXPATH",
305 : "UNIXPATH value required");
306 0 : return GNUNET_SYSERR;
307 : }
308 0 : if (strlen (serve_unixpath) >= sizeof (s_un.sun_path))
309 : {
310 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
311 : "unixpath `%s' is too long\n",
312 : serve_unixpath);
313 0 : GNUNET_free (serve_unixpath);
314 0 : return GNUNET_SYSERR;
315 : }
316 :
317 0 : if (GNUNET_OK !=
318 0 : GNUNET_CONFIGURATION_get_value_string (cfg,
319 : section,
320 : "UNIXPATH_MODE",
321 : &modestring))
322 : {
323 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
324 : section,
325 : "UNIXPATH_MODE");
326 0 : GNUNET_free (serve_unixpath);
327 0 : return GNUNET_SYSERR;
328 : }
329 0 : errno = 0;
330 0 : unixpath_mode = (mode_t) strtoul (modestring,
331 : NULL,
332 : 8);
333 0 : if (0 != errno)
334 : {
335 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
336 : section,
337 : "UNIXPATH_MODE",
338 : "must be octal number");
339 0 : GNUNET_free (modestring);
340 0 : GNUNET_free (serve_unixpath);
341 0 : return GNUNET_SYSERR;
342 : }
343 0 : GNUNET_free (modestring);
344 :
345 : {
346 : int fd;
347 :
348 0 : fd = open_unix_path (serve_unixpath,
349 : unixpath_mode);
350 0 : GNUNET_free (serve_unixpath);
351 0 : if (-1 == fd)
352 0 : return GNUNET_NO;
353 0 : cb (cb_cls,
354 : fd);
355 0 : return GNUNET_OK;
356 : }
357 : }
358 :
359 57 : if (0 == strcasecmp (serve_type,
360 : "tcp"))
361 : {
362 : unsigned long long lport;
363 : struct GNUNET_NETWORK_Handle *nh;
364 : char *bind_to;
365 :
366 57 : if (GNUNET_OK !=
367 57 : GNUNET_CONFIGURATION_get_value_number (cfg,
368 : section,
369 : "PORT",
370 : &lport))
371 : {
372 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
373 : section,
374 : "PORT",
375 : "port number required");
376 0 : return GNUNET_SYSERR;
377 : }
378 :
379 57 : if ( (0 == lport) ||
380 57 : (lport > UINT16_MAX) )
381 : {
382 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
383 : section,
384 : "PORT",
385 : "port number not in [1,65535]");
386 0 : return GNUNET_SYSERR;
387 : }
388 :
389 57 : if (GNUNET_OK !=
390 57 : GNUNET_CONFIGURATION_get_value_string (cfg,
391 : section,
392 : "BIND_TO",
393 : &bind_to))
394 57 : bind_to = NULL;
395 :
396 : /* let's have fun binding... */
397 : {
398 : char port_str[6];
399 : struct addrinfo hints;
400 : struct addrinfo *res;
401 : int ec;
402 :
403 57 : GNUNET_snprintf (port_str,
404 : sizeof (port_str),
405 : "%u",
406 : (unsigned int) lport);
407 57 : memset (&hints,
408 : 0,
409 : sizeof (hints));
410 57 : hints.ai_family = AF_UNSPEC;
411 57 : hints.ai_socktype = SOCK_STREAM;
412 57 : hints.ai_protocol = IPPROTO_TCP;
413 57 : hints.ai_flags = AI_PASSIVE
414 : #ifdef AI_IDN
415 : | AI_IDN
416 : #endif
417 : ;
418 :
419 57 : if (0 !=
420 57 : (ec = getaddrinfo (bind_to,
421 : port_str,
422 : &hints,
423 : &res)))
424 : {
425 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
426 : "Failed to resolve BIND_TO address `%s': %s\n",
427 : bind_to,
428 : gai_strerror (ec));
429 0 : GNUNET_free (bind_to);
430 0 : return GNUNET_SYSERR;
431 : }
432 57 : GNUNET_free (bind_to);
433 57 : for (struct addrinfo *ai = res;
434 171 : NULL != ai;
435 114 : ai = ai->ai_next)
436 : {
437 114 : if (NULL == (nh = GNUNET_NETWORK_socket_create (ai->ai_family,
438 : ai->ai_socktype,
439 : ai->ai_protocol)))
440 : {
441 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
442 : "socket");
443 0 : freeaddrinfo (res);
444 0 : return GNUNET_NO;
445 : }
446 : {
447 114 : const int on = 1;
448 :
449 114 : if (GNUNET_OK !=
450 114 : GNUNET_NETWORK_socket_setsockopt (nh,
451 : SOL_SOCKET,
452 : SO_REUSEPORT,
453 : &on,
454 : sizeof(on)))
455 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
456 : "setsockopt");
457 : }
458 114 : if (GNUNET_OK !=
459 114 : GNUNET_NETWORK_socket_bind (nh,
460 114 : ai->ai_addr,
461 : ai->ai_addrlen))
462 : {
463 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
464 : "bind");
465 0 : freeaddrinfo (res);
466 0 : return GNUNET_NO;
467 : }
468 :
469 114 : if (GNUNET_OK !=
470 114 : GNUNET_NETWORK_socket_listen (nh,
471 : LISTEN_BACKLOG))
472 : {
473 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
474 : "listen");
475 0 : GNUNET_SCHEDULER_shutdown ();
476 0 : return GNUNET_NO;
477 : }
478 :
479 : /* extract and return actual socket handle from 'nh' */
480 : {
481 : int fh;
482 :
483 114 : fh = GNUNET_NETWORK_get_fd (nh);
484 114 : GNUNET_NETWORK_socket_free_memory_only_ (nh);
485 114 : if (-1 == fh)
486 : {
487 0 : GNUNET_break (0);
488 0 : return GNUNET_NO;
489 : }
490 114 : cb (cb_cls,
491 : fh);
492 : }
493 : } /* for all addrinfos */
494 57 : freeaddrinfo (res);
495 57 : return GNUNET_OK;
496 : } /* bind data scope */
497 : } /* tcp */
498 0 : return ret;
499 : }
|