mhd_config.c (15100B)
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 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 { 331 char *endp; 332 unsigned long mode; 333 334 errno = 0; 335 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 if ( (0 != errno) || 341 (endp == modestring) || 342 ('\0' != *endp) || 343 (mode != (unsigned long) (mode_t) mode) ) 344 { 345 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 346 section, 347 "UNIXPATH_MODE", 348 "must be octal number"); 349 GNUNET_free (modestring); 350 GNUNET_free (serve_unixpath); 351 return GNUNET_SYSERR; 352 } 353 unixpath_mode = (mode_t) mode; 354 } 355 GNUNET_free (modestring); 356 357 { 358 int fd; 359 360 fd = open_unix_path (serve_unixpath, 361 unixpath_mode); 362 GNUNET_free (serve_unixpath); 363 if (-1 == fd) 364 return GNUNET_NO; 365 cb (cb_cls, 366 fd); 367 return GNUNET_OK; 368 } 369 } 370 371 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 if (GNUNET_OK != 379 GNUNET_CONFIGURATION_get_value_number (cfg, 380 section, 381 "PORT", 382 &lport)) 383 { 384 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 385 section, 386 "PORT", 387 "port number required"); 388 return GNUNET_SYSERR; 389 } 390 391 if ( (0 == lport) || 392 (lport > UINT16_MAX) ) 393 { 394 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 395 section, 396 "PORT", 397 "port number not in [1,65535]"); 398 return GNUNET_SYSERR; 399 } 400 401 if (GNUNET_OK != 402 GNUNET_CONFIGURATION_get_value_string (cfg, 403 section, 404 "BIND_TO", 405 &bind_to)) 406 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 GNUNET_snprintf (port_str, 416 sizeof (port_str), 417 "%u", 418 (unsigned int) lport); 419 memset (&hints, 420 0, 421 sizeof (hints)); 422 hints.ai_family = AF_UNSPEC; 423 hints.ai_socktype = SOCK_STREAM; 424 hints.ai_protocol = IPPROTO_TCP; 425 hints.ai_flags = AI_PASSIVE 426 #ifdef AI_IDN 427 | AI_IDN 428 #endif 429 ; 430 431 if (0 != 432 (ec = getaddrinfo (bind_to, 433 port_str, 434 &hints, 435 &res))) 436 { 437 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 438 "Failed to resolve BIND_TO address `%s': %s\n", 439 bind_to, 440 gai_strerror (ec)); 441 GNUNET_free (bind_to); 442 return GNUNET_SYSERR; 443 } 444 GNUNET_free (bind_to); 445 for (struct addrinfo *ai = res; 446 NULL != ai; 447 ai = ai->ai_next) 448 { 449 if (NULL == (nh = GNUNET_NETWORK_socket_create (ai->ai_family, 450 ai->ai_socktype, 451 ai->ai_protocol))) 452 { 453 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 454 "socket"); 455 freeaddrinfo (res); 456 return GNUNET_NO; 457 } 458 { 459 const int on = 1; 460 461 if (GNUNET_OK != 462 GNUNET_NETWORK_socket_setsockopt (nh, 463 SOL_SOCKET, 464 SO_REUSEPORT, 465 &on, 466 sizeof(on))) 467 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 468 "setsockopt"); 469 } 470 if (GNUNET_OK != 471 GNUNET_NETWORK_socket_bind (nh, 472 ai->ai_addr, 473 ai->ai_addrlen)) 474 { 475 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 476 "bind"); 477 GNUNET_break (GNUNET_OK == 478 GNUNET_NETWORK_socket_close (nh)); 479 freeaddrinfo (res); 480 return GNUNET_NO; 481 } 482 483 if (GNUNET_OK != 484 GNUNET_NETWORK_socket_listen (nh, 485 LISTEN_BACKLOG)) 486 { 487 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 488 "listen"); 489 GNUNET_SCHEDULER_shutdown (); 490 GNUNET_break (GNUNET_OK == 491 GNUNET_NETWORK_socket_close (nh)); 492 freeaddrinfo (res); 493 return GNUNET_NO; 494 } 495 496 /* extract and return actual socket handle from 'nh' */ 497 { 498 int fh; 499 500 fh = GNUNET_NETWORK_get_fd (nh); 501 GNUNET_NETWORK_socket_free_memory_only_ (nh); 502 if (-1 == fh) 503 { 504 GNUNET_break (0); 505 freeaddrinfo (res); 506 return GNUNET_NO; 507 } 508 cb (cb_cls, 509 fh); 510 } 511 } /* for all addrinfos */ 512 freeaddrinfo (res); 513 return GNUNET_OK; 514 } /* bind data scope */ 515 } /* tcp */ 516 return ret; 517 }