mhd_config.c (14737B)
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 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 if (-1 == cache) 51 return; 52 if (0 == cache) 53 { 54 if (0 == 55 GNUNET_get_log_call_status (GNUNET_ERROR_TYPE_INFO, 56 "libmicrohttpd", 57 __FILE__, 58 __FUNCTION__, 59 __LINE__)) 60 { 61 cache = -1; 62 return; 63 } 64 } 65 cache = 1; 66 vsnprintf (buf, 67 sizeof (buf), 68 fm, 69 ap); 70 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 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 if (sizeof (un->sun_path) <= strlen (unix_path)) 93 { 94 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 95 "unixpath `%s' is too long\n", 96 unix_path); 97 return -1; 98 } 99 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 100 "Creating listen socket '%s' with mode %o\n", 101 unix_path, 102 unix_mode); 103 104 if (GNUNET_OK != 105 GNUNET_DISK_directory_create_for_file (unix_path)) 106 { 107 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 108 "mkdir", 109 unix_path); 110 } 111 112 un = GNUNET_new (struct sockaddr_un); 113 un->sun_family = AF_UNIX; 114 strncpy (un->sun_path, 115 unix_path, 116 sizeof (un->sun_path) - 1); 117 GNUNET_NETWORK_unix_precheck (un); 118 119 if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX, 120 SOCK_STREAM, 121 0))) 122 { 123 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 124 "socket"); 125 GNUNET_free (un); 126 return -1; 127 } 128 129 if (GNUNET_OK != 130 GNUNET_NETWORK_socket_bind (nh, 131 (void *) un, 132 sizeof (struct sockaddr_un))) 133 { 134 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 135 "bind", 136 unix_path); 137 GNUNET_free (un); 138 GNUNET_NETWORK_socket_close (nh); 139 return -1; 140 } 141 GNUNET_free (un); 142 if (GNUNET_OK != 143 GNUNET_NETWORK_socket_listen (nh, 144 LISTEN_BACKLOG)) 145 { 146 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 147 "listen"); 148 GNUNET_NETWORK_socket_close (nh); 149 return -1; 150 } 151 152 if (0 != chmod (unix_path, 153 unix_mode)) 154 { 155 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 156 "chmod"); 157 GNUNET_NETWORK_socket_close (nh); 158 return -1; 159 } 160 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 fd = GNUNET_NETWORK_get_fd (nh); 170 GNUNET_NETWORK_socket_free_memory_only_ (nh); 171 return fd; 172 } 173 } 174 175 176 enum GNUNET_GenericReturnValue 177 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 const char *choices[] = { 183 "tcp", 184 "unix", 185 "systemd", 186 NULL 187 }; 188 const char *serve_type; 189 enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR; 190 191 if (GNUNET_OK != 192 GNUNET_CONFIGURATION_get_value_choice (cfg, 193 section, 194 "SERVE", 195 choices, 196 &serve_type)) 197 { 198 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 199 section, 200 "SERVE", 201 "serve type (tcp, unix or systemd) required"); 202 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 if ( (NULL != (listen_pid = getenv ("LISTEN_PID"))) && 214 (getpid () == 215 strtol (listen_pid, 216 NULL, 217 10)) && 218 (NULL != (listen_fds = getenv ("LISTEN_FDS"))) && 219 (NULL != (listen_fdn = getenv ("LISTEN_FDNAMES"))) ) 220 { 221 int off = 3; 222 unsigned int cnt; 223 char dummy; 224 225 if (0 != strcmp (serve_type, 226 "systemd")) 227 { 228 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 ret = GNUNET_NO; 234 } 235 if (1 != sscanf (listen_fds, 236 "%u%c", 237 &cnt, 238 &dummy)) 239 { 240 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 fdns = GNUNET_strdup (listen_fdn); 249 for (const char *tok = strtok (fdns, 250 ":"); 251 cnt-- > 0; 252 tok = strtok (NULL, 253 ":")) 254 { 255 int fh = off++; 256 int flags; 257 258 flags = fcntl (fh, 259 F_GETFD); 260 if ( (-1 == flags) && 261 (EBADF == errno) ) 262 { 263 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 264 "Bad listen socket %s (%d) passed, ignored\n", 265 tok, 266 fh); 267 continue; 268 } 269 flags |= FD_CLOEXEC; 270 if (0 != fcntl (fh, 271 F_SETFD, 272 flags)) 273 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 274 "fcntl"); 275 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 276 "Successfully obtained listen socket %s (#%d) from hypervisor\n", 277 tok, 278 fh); 279 cb (cb_cls, 280 fh); 281 ret = GNUNET_OK; 282 } 283 GNUNET_free (fdns); 284 } 285 } 286 } 287 288 /* now try configuration file */ 289 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 if (GNUNET_OK != 298 GNUNET_CONFIGURATION_get_value_filename (cfg, 299 section, 300 "UNIXPATH", 301 &serve_unixpath)) 302 { 303 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 304 section, 305 "UNIXPATH", 306 "UNIXPATH value required"); 307 return GNUNET_SYSERR; 308 } 309 if (strlen (serve_unixpath) >= sizeof (s_un.sun_path)) 310 { 311 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 312 "unixpath `%s' is too long\n", 313 serve_unixpath); 314 GNUNET_free (serve_unixpath); 315 return GNUNET_SYSERR; 316 } 317 318 if (GNUNET_OK != 319 GNUNET_CONFIGURATION_get_value_string (cfg, 320 section, 321 "UNIXPATH_MODE", 322 &modestring)) 323 { 324 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 325 section, 326 "UNIXPATH_MODE"); 327 GNUNET_free (serve_unixpath); 328 return GNUNET_SYSERR; 329 } 330 errno = 0; 331 unixpath_mode = (mode_t) strtoul (modestring, 332 NULL, 333 8); 334 if (0 != errno) 335 { 336 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 337 section, 338 "UNIXPATH_MODE", 339 "must be octal number"); 340 GNUNET_free (modestring); 341 GNUNET_free (serve_unixpath); 342 return GNUNET_SYSERR; 343 } 344 GNUNET_free (modestring); 345 346 { 347 int fd; 348 349 fd = open_unix_path (serve_unixpath, 350 unixpath_mode); 351 GNUNET_free (serve_unixpath); 352 if (-1 == fd) 353 return GNUNET_NO; 354 cb (cb_cls, 355 fd); 356 return GNUNET_OK; 357 } 358 } 359 360 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 if (GNUNET_OK != 368 GNUNET_CONFIGURATION_get_value_number (cfg, 369 section, 370 "PORT", 371 &lport)) 372 { 373 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 374 section, 375 "PORT", 376 "port number required"); 377 return GNUNET_SYSERR; 378 } 379 380 if ( (0 == lport) || 381 (lport > UINT16_MAX) ) 382 { 383 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 384 section, 385 "PORT", 386 "port number not in [1,65535]"); 387 return GNUNET_SYSERR; 388 } 389 390 if (GNUNET_OK != 391 GNUNET_CONFIGURATION_get_value_string (cfg, 392 section, 393 "BIND_TO", 394 &bind_to)) 395 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 GNUNET_snprintf (port_str, 405 sizeof (port_str), 406 "%u", 407 (unsigned int) lport); 408 memset (&hints, 409 0, 410 sizeof (hints)); 411 hints.ai_family = AF_UNSPEC; 412 hints.ai_socktype = SOCK_STREAM; 413 hints.ai_protocol = IPPROTO_TCP; 414 hints.ai_flags = AI_PASSIVE 415 #ifdef AI_IDN 416 | AI_IDN 417 #endif 418 ; 419 420 if (0 != 421 (ec = getaddrinfo (bind_to, 422 port_str, 423 &hints, 424 &res))) 425 { 426 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 427 "Failed to resolve BIND_TO address `%s': %s\n", 428 bind_to, 429 gai_strerror (ec)); 430 GNUNET_free (bind_to); 431 return GNUNET_SYSERR; 432 } 433 GNUNET_free (bind_to); 434 for (struct addrinfo *ai = res; 435 NULL != ai; 436 ai = ai->ai_next) 437 { 438 if (NULL == (nh = GNUNET_NETWORK_socket_create (ai->ai_family, 439 ai->ai_socktype, 440 ai->ai_protocol))) 441 { 442 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 443 "socket"); 444 freeaddrinfo (res); 445 return GNUNET_NO; 446 } 447 { 448 const int on = 1; 449 450 if (GNUNET_OK != 451 GNUNET_NETWORK_socket_setsockopt (nh, 452 SOL_SOCKET, 453 SO_REUSEPORT, 454 &on, 455 sizeof(on))) 456 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 457 "setsockopt"); 458 } 459 if (GNUNET_OK != 460 GNUNET_NETWORK_socket_bind (nh, 461 ai->ai_addr, 462 ai->ai_addrlen)) 463 { 464 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 465 "bind"); 466 GNUNET_break (GNUNET_OK == 467 GNUNET_NETWORK_socket_close (nh)); 468 freeaddrinfo (res); 469 return GNUNET_NO; 470 } 471 472 if (GNUNET_OK != 473 GNUNET_NETWORK_socket_listen (nh, 474 LISTEN_BACKLOG)) 475 { 476 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 477 "listen"); 478 GNUNET_SCHEDULER_shutdown (); 479 GNUNET_break (GNUNET_OK == 480 GNUNET_NETWORK_socket_close (nh)); 481 freeaddrinfo (res); 482 return GNUNET_NO; 483 } 484 485 /* extract and return actual socket handle from 'nh' */ 486 { 487 int fh; 488 489 fh = GNUNET_NETWORK_get_fd (nh); 490 GNUNET_NETWORK_socket_free_memory_only_ (nh); 491 if (-1 == fh) 492 { 493 GNUNET_break (0); 494 return GNUNET_NO; 495 } 496 cb (cb_cls, 497 fh); 498 } 499 } /* for all addrinfos */ 500 freeaddrinfo (res); 501 return GNUNET_OK; 502 } /* bind data scope */ 503 } /* tcp */ 504 return ret; 505 }