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