Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014--2020 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"
24 : #include <gnunet/gnunet_util_lib.h>
25 : #include "taler_mhd_lib.h"
26 :
27 :
28 : /**
29 : * Backlog for listen operation on UNIX domain sockets.
30 : */
31 : #define UNIX_BACKLOG 500
32 :
33 :
34 : /**
35 : * Parse the configuration to determine on which port
36 : * or UNIX domain path we should run an HTTP service.
37 : *
38 : * @param cfg configuration to parse
39 : * @param section section of the configuration to parse (usually "exchange")
40 : * @param[out] rport set to the port number, or 0 for none
41 : * @param[out] unix_path set to the UNIX path, or NULL for none
42 : * @param[out] unix_mode set to the mode to be used for @a unix_path
43 : * @return #GNUNET_OK on success
44 : */
45 : enum GNUNET_GenericReturnValue
46 0 : TALER_MHD_parse_config (const struct GNUNET_CONFIGURATION_Handle *cfg,
47 : const char *section,
48 : uint16_t *rport,
49 : char **unix_path,
50 : mode_t *unix_mode)
51 : {
52 0 : const char *choices[] = {
53 : "tcp",
54 : "unix",
55 : NULL
56 : };
57 : const char *serve_type;
58 :
59 0 : if (GNUNET_OK !=
60 0 : GNUNET_CONFIGURATION_get_value_choice (cfg,
61 : section,
62 : "SERVE",
63 : choices,
64 : &serve_type))
65 : {
66 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
67 : section,
68 : "SERVE",
69 : "serve type (tcp or unix) required");
70 0 : return GNUNET_SYSERR;
71 : }
72 :
73 0 : if (0 == strcasecmp (serve_type,
74 : "tcp"))
75 : {
76 : unsigned long long port;
77 :
78 0 : if (GNUNET_OK !=
79 0 : GNUNET_CONFIGURATION_get_value_number (cfg,
80 : section,
81 : "port",
82 : &port))
83 : {
84 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
85 : section,
86 : "PORT",
87 : "port number required");
88 0 : return GNUNET_SYSERR;
89 : }
90 :
91 0 : if ( (0 == port) ||
92 0 : (port > UINT16_MAX) )
93 : {
94 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
95 : section,
96 : "PORT",
97 : "port number not in [1,65535]");
98 0 : return GNUNET_SYSERR;
99 : }
100 0 : *rport = (uint16_t) port;
101 0 : *unix_path = NULL;
102 0 : return GNUNET_OK;
103 : }
104 0 : if (0 == strcmp (serve_type,
105 : "unix"))
106 : {
107 : struct sockaddr_un s_un;
108 : char *modestring;
109 :
110 0 : if (GNUNET_OK !=
111 0 : GNUNET_CONFIGURATION_get_value_filename (cfg,
112 : section,
113 : "UNIXPATH",
114 : unix_path))
115 : {
116 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
117 : section,
118 : "UNIXPATH",
119 : "UNIXPATH value required");
120 0 : return GNUNET_SYSERR;
121 : }
122 0 : if (strlen (*unix_path) >= sizeof (s_un.sun_path))
123 : {
124 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
125 : "unixpath `%s' is too long\n",
126 : *unix_path);
127 0 : GNUNET_free (*unix_path);
128 0 : return GNUNET_SYSERR;
129 : }
130 :
131 0 : if (GNUNET_OK !=
132 0 : GNUNET_CONFIGURATION_get_value_string (cfg,
133 : section,
134 : "UNIXPATH_MODE",
135 : &modestring))
136 : {
137 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
138 : section,
139 : "UNIXPATH_MODE");
140 0 : GNUNET_free (*unix_path);
141 0 : return GNUNET_SYSERR;
142 : }
143 0 : errno = 0;
144 0 : *unix_mode = (mode_t) strtoul (modestring, NULL, 8);
145 0 : if (0 != errno)
146 : {
147 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
148 : section,
149 : "UNIXPATH_MODE",
150 : "must be octal number");
151 0 : GNUNET_free (modestring);
152 0 : GNUNET_free (*unix_path);
153 0 : return GNUNET_SYSERR;
154 : }
155 0 : GNUNET_free (modestring);
156 0 : return GNUNET_OK;
157 : }
158 : /* not reached */
159 0 : GNUNET_assert (0);
160 : return GNUNET_SYSERR;
161 : }
162 :
163 :
164 : /**
165 : * Function called for logging by MHD.
166 : *
167 : * @param cls closure, NULL
168 : * @param fm format string (`printf()`-style)
169 : * @param ap arguments to @a fm
170 : */
171 : void
172 0 : TALER_MHD_handle_logs (void *cls,
173 : const char *fm,
174 : va_list ap)
175 : {
176 : static int cache;
177 : char buf[2048];
178 :
179 : (void) cls;
180 0 : if (-1 == cache)
181 0 : return;
182 0 : if (0 == cache)
183 : {
184 0 : if (0 ==
185 0 : GNUNET_get_log_call_status (GNUNET_ERROR_TYPE_INFO,
186 : "libmicrohttpd",
187 : __FILE__,
188 : __FUNCTION__,
189 : __LINE__))
190 : {
191 0 : cache = -1;
192 0 : return;
193 : }
194 : }
195 0 : cache = 1;
196 0 : vsnprintf (buf,
197 : sizeof (buf),
198 : fm,
199 : ap);
200 0 : GNUNET_log_from_nocheck (GNUNET_ERROR_TYPE_INFO,
201 : "libmicrohttpd",
202 : "%s",
203 : buf);
204 : }
205 :
206 :
207 : /**
208 : * Open UNIX domain socket for listining at @a unix_path with
209 : * permissions @a unix_mode.
210 : *
211 : * @param unix_path where to listen
212 : * @param unix_mode access permissions to set
213 : * @return -1 on error, otherwise the listen socket
214 : */
215 : int
216 0 : TALER_MHD_open_unix_path (const char *unix_path,
217 : mode_t unix_mode)
218 : {
219 : struct GNUNET_NETWORK_Handle *nh;
220 : struct sockaddr_un *un;
221 :
222 0 : if (sizeof (un->sun_path) <= strlen (unix_path))
223 : {
224 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
225 : "unixpath `%s' is too long\n",
226 : unix_path);
227 0 : return -1;
228 : }
229 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
230 : "Creating listen socket '%s' with mode %o\n",
231 : unix_path,
232 : unix_mode);
233 :
234 0 : if (GNUNET_OK !=
235 0 : GNUNET_DISK_directory_create_for_file (unix_path))
236 : {
237 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
238 : "mkdir",
239 : unix_path);
240 : }
241 :
242 0 : un = GNUNET_new (struct sockaddr_un);
243 0 : un->sun_family = AF_UNIX;
244 0 : strncpy (un->sun_path,
245 : unix_path,
246 : sizeof (un->sun_path) - 1);
247 0 : GNUNET_NETWORK_unix_precheck (un);
248 :
249 0 : if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX,
250 : SOCK_STREAM,
251 : 0)))
252 : {
253 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
254 : "socket");
255 0 : GNUNET_free (un);
256 0 : return -1;
257 : }
258 :
259 0 : if (GNUNET_OK !=
260 0 : GNUNET_NETWORK_socket_bind (nh,
261 : (void *) un,
262 : sizeof (struct sockaddr_un)))
263 : {
264 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
265 : "bind",
266 : unix_path);
267 0 : GNUNET_free (un);
268 0 : GNUNET_NETWORK_socket_close (nh);
269 0 : return -1;
270 : }
271 0 : GNUNET_free (un);
272 0 : if (GNUNET_OK !=
273 0 : GNUNET_NETWORK_socket_listen (nh,
274 : UNIX_BACKLOG))
275 : {
276 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
277 : "listen");
278 0 : GNUNET_NETWORK_socket_close (nh);
279 0 : return -1;
280 : }
281 :
282 0 : if (0 != chmod (unix_path,
283 : unix_mode))
284 : {
285 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
286 : "chmod");
287 0 : GNUNET_NETWORK_socket_close (nh);
288 0 : return -1;
289 : }
290 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
291 : "set socket '%s' to mode %o\n",
292 : unix_path,
293 : unix_mode);
294 :
295 : /* extract and return actual socket handle from 'nh' */
296 : {
297 : int fd;
298 :
299 0 : fd = GNUNET_NETWORK_get_fd (nh);
300 0 : GNUNET_NETWORK_socket_free_memory_only_ (nh);
301 0 : return fd;
302 : }
303 : }
304 :
305 :
306 : /**
307 : * Bind a listen socket to the UNIX domain path or the TCP port and IP address
308 : * as specified in @a cfg in section @a section. IF only a port was
309 : * specified, set @a port and return -1. Otherwise, return the bound file
310 : * descriptor.
311 : *
312 : * @param cfg configuration to parse
313 : * @param section configuration section to use
314 : * @param[out] port port to set, if TCP without BINDTO
315 : * @return -1 and a port of zero on error, otherwise
316 : * either -1 and a port, or a bound stream socket
317 : */
318 : int
319 0 : TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
320 : const char *section,
321 : uint16_t *port)
322 : {
323 : char *bind_to;
324 : struct GNUNET_NETWORK_Handle *nh;
325 :
326 : /* try systemd passing first */
327 : {
328 : const char *listen_pid;
329 : const char *listen_fds;
330 :
331 : /* check for systemd-style FD passing */
332 0 : listen_pid = getenv ("LISTEN_PID");
333 0 : listen_fds = getenv ("LISTEN_FDS");
334 0 : if ( (NULL != listen_pid) &&
335 0 : (NULL != listen_fds) &&
336 0 : (getpid () == strtol (listen_pid,
337 : NULL,
338 0 : 10)) &&
339 0 : (1 == strtoul (listen_fds,
340 : NULL,
341 : 10)) )
342 : {
343 : int fh;
344 : int flags;
345 :
346 0 : fh = 3;
347 0 : flags = fcntl (fh,
348 : F_GETFD);
349 0 : if ( (-1 == flags) &&
350 0 : (EBADF == errno) )
351 : {
352 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
353 : "Bad listen socket passed, ignored\n");
354 0 : fh = -1;
355 : }
356 0 : flags |= FD_CLOEXEC;
357 0 : if ( (-1 != fh) &&
358 0 : (0 != fcntl (fh,
359 : F_SETFD,
360 : flags)) )
361 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
362 : "fcntl");
363 0 : if (-1 != fh)
364 : {
365 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
366 : "Successfully obtained listen socket from hypervisor\n");
367 0 : return fh;
368 : }
369 : }
370 : }
371 :
372 : /* now try configuration file */
373 0 : *port = 0;
374 : {
375 : char *serve_unixpath;
376 : mode_t unixpath_mode;
377 :
378 0 : if (GNUNET_OK !=
379 0 : TALER_MHD_parse_config (cfg,
380 : section,
381 : port,
382 : &serve_unixpath,
383 : &unixpath_mode))
384 0 : return -1;
385 0 : if (NULL != serve_unixpath)
386 : {
387 : int ret;
388 :
389 0 : ret = TALER_MHD_open_unix_path (serve_unixpath,
390 : unixpath_mode);
391 0 : GNUNET_free (serve_unixpath);
392 0 : return ret;
393 : }
394 : }
395 0 : if (GNUNET_OK !=
396 0 : GNUNET_CONFIGURATION_get_value_string (cfg,
397 : section,
398 : "BIND_TO",
399 : &bind_to))
400 0 : return -1; /* only set port */
401 :
402 : /* let's have fun binding... */
403 : {
404 : char port_str[6];
405 : struct addrinfo hints;
406 : struct addrinfo *res;
407 : int ec;
408 :
409 0 : GNUNET_snprintf (port_str,
410 : sizeof (port_str),
411 : "%u",
412 0 : (unsigned int) *port);
413 0 : *port = 0; /* do NOT return port in case of errors */
414 0 : memset (&hints,
415 : 0,
416 : sizeof (hints));
417 0 : hints.ai_family = AF_UNSPEC;
418 0 : hints.ai_socktype = SOCK_STREAM;
419 0 : hints.ai_protocol = IPPROTO_TCP;
420 0 : hints.ai_flags = AI_PASSIVE
421 : #ifdef AI_IDN
422 : | AI_IDN
423 : #endif
424 : ;
425 :
426 0 : if (0 !=
427 0 : (ec = getaddrinfo (bind_to,
428 : port_str,
429 : &hints,
430 : &res)))
431 : {
432 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
433 : "Failed to resolve BIND_TO address `%s': %s\n",
434 : bind_to,
435 : gai_strerror (ec));
436 0 : GNUNET_free (bind_to);
437 0 : return -1;
438 : }
439 0 : GNUNET_free (bind_to);
440 :
441 0 : if (NULL == (nh = GNUNET_NETWORK_socket_create (res->ai_family,
442 0 : res->ai_socktype,
443 0 : res->ai_protocol)))
444 : {
445 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
446 : "socket");
447 0 : freeaddrinfo (res);
448 0 : return -1;
449 : }
450 : {
451 0 : const int on = 1;
452 :
453 0 : if (GNUNET_OK !=
454 0 : GNUNET_NETWORK_socket_setsockopt (nh,
455 : SOL_SOCKET,
456 : SO_REUSEPORT,
457 : &on,
458 : sizeof(on)))
459 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
460 : "setsockopt");
461 : }
462 0 : if (GNUNET_OK !=
463 0 : GNUNET_NETWORK_socket_bind (nh,
464 0 : res->ai_addr,
465 0 : res->ai_addrlen))
466 : {
467 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
468 : "bind");
469 0 : freeaddrinfo (res);
470 0 : return -1;
471 : }
472 0 : freeaddrinfo (res);
473 : }
474 :
475 0 : if (GNUNET_OK !=
476 0 : GNUNET_NETWORK_socket_listen (nh,
477 : UNIX_BACKLOG))
478 : {
479 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
480 : "listen");
481 0 : GNUNET_SCHEDULER_shutdown ();
482 0 : return -1;
483 : }
484 :
485 : /* extract and return actual socket handle from 'nh' */
486 : {
487 : int fh;
488 :
489 0 : fh = GNUNET_NETWORK_get_fd (nh);
490 0 : GNUNET_NETWORK_socket_free_memory_only_ (nh);
491 0 : return fh;
492 : }
493 : }
|