1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 2/* dbus-server-socket.c Server implementation for sockets 3 * 4 * Copyright (C) 2002, 2003, 2004, 2006 Red Hat Inc. 5 * 6 * Licensed under the Academic Free License version 2.1 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 * 22 */ 23 24#include <config.h> 25#include "dbus-internals.h" 26#include "dbus-server-socket.h" 27#include "dbus-transport-socket.h" 28#include "dbus-connection-internal.h" 29#include "dbus-memory.h" 30#include "dbus-nonce.h" 31#include "dbus-string.h" 32 33/** 34 * @defgroup DBusServerSocket DBusServer implementations for SOCKET 35 * @ingroup DBusInternals 36 * @brief Implementation details of DBusServer on SOCKET 37 * 38 * @{ 39 */ 40/** 41 * 42 * Opaque object representing a Socket server implementation. 43 */ 44typedef struct DBusServerSocket DBusServerSocket; 45 46/** 47 * Implementation details of DBusServerSocket. All members 48 * are private. 49 */ 50struct DBusServerSocket 51{ 52 DBusServer base; /**< Parent class members. */ 53 int n_fds; /**< Number of active file handles */ 54 int *fds; /**< File descriptor or -1 if disconnected. */ 55 DBusWatch **watch; /**< File descriptor watch. */ 56 char *socket_name; /**< Name of domain socket, to unlink if appropriate */ 57 DBusNonceFile *noncefile; /**< Nonce file used to authenticate clients */ 58}; 59 60static void 61socket_finalize (DBusServer *server) 62{ 63 DBusServerSocket *socket_server = (DBusServerSocket*) server; 64 int i; 65 66 _dbus_server_finalize_base (server); 67 68 for (i = 0 ; i < socket_server->n_fds ; i++) 69 if (socket_server->watch[i]) 70 { 71 _dbus_watch_unref (socket_server->watch[i]); 72 socket_server->watch[i] = NULL; 73 } 74 75 dbus_free (socket_server->fds); 76 dbus_free (socket_server->watch); 77 dbus_free (socket_server->socket_name); 78 if (socket_server->noncefile) 79 _dbus_noncefile_delete (socket_server->noncefile, NULL); 80 dbus_free (socket_server->noncefile); 81 dbus_free (server); 82} 83 84/* Return value is just for memory, not other failures. */ 85static dbus_bool_t 86handle_new_client_fd_and_unlock (DBusServer *server, 87 int client_fd) 88{ 89 DBusConnection *connection; 90 DBusTransport *transport; 91 DBusNewConnectionFunction new_connection_function; 92 void *new_connection_data; 93 94 _dbus_verbose ("Creating new client connection with fd %d\n", client_fd); 95 96 HAVE_LOCK_CHECK (server); 97 98 if (!_dbus_set_fd_nonblocking (client_fd, NULL)) 99 { 100 SERVER_UNLOCK (server); 101 return TRUE; 102 } 103 104 transport = _dbus_transport_new_for_socket (client_fd, &server->guid_hex, FALSE); 105 if (transport == NULL) 106 { 107 _dbus_close_socket (client_fd, NULL); 108 SERVER_UNLOCK (server); 109 return FALSE; 110 } 111 112 if (!_dbus_transport_set_auth_mechanisms (transport, 113 (const char **) server->auth_mechanisms)) 114 { 115 _dbus_transport_unref (transport); 116 SERVER_UNLOCK (server); 117 return FALSE; 118 } 119 120 /* note that client_fd is now owned by the transport, and will be 121 * closed on transport disconnection/finalization 122 */ 123 124 connection = _dbus_connection_new_for_transport (transport); 125 _dbus_transport_unref (transport); 126 transport = NULL; /* now under the connection lock */ 127 128 if (connection == NULL) 129 { 130 SERVER_UNLOCK (server); 131 return FALSE; 132 } 133 134 /* See if someone wants to handle this new connection, self-referencing 135 * for paranoia. 136 */ 137 new_connection_function = server->new_connection_function; 138 new_connection_data = server->new_connection_data; 139 140 _dbus_server_ref_unlocked (server); 141 SERVER_UNLOCK (server); 142 143 if (new_connection_function) 144 { 145 (* new_connection_function) (server, connection, 146 new_connection_data); 147 } 148 dbus_server_unref (server); 149 150 /* If no one grabbed a reference, the connection will die. */ 151 _dbus_connection_close_if_only_one_ref (connection); 152 dbus_connection_unref (connection); 153 154 return TRUE; 155} 156 157static dbus_bool_t 158socket_handle_watch (DBusWatch *watch, 159 unsigned int flags, 160 void *data) 161{ 162 DBusServer *server = data; 163 DBusServerSocket *socket_server = data; 164 165#ifndef DBUS_DISABLE_ASSERT 166 int i; 167 dbus_bool_t found = FALSE; 168#endif 169 170 SERVER_LOCK (server); 171 172#ifndef DBUS_DISABLE_ASSERT 173 for (i = 0 ; i < socket_server->n_fds ; i++) 174 { 175 if (socket_server->watch[i] == watch) 176 found = TRUE; 177 } 178 _dbus_assert (found); 179#endif 180 181 _dbus_verbose ("Handling client connection, flags 0x%x\n", flags); 182 183 if (flags & DBUS_WATCH_READABLE) 184 { 185 int client_fd; 186 int listen_fd; 187 188 listen_fd = dbus_watch_get_socket (watch); 189 190 if (socket_server->noncefile) 191 client_fd = _dbus_accept_with_noncefile (listen_fd, socket_server->noncefile); 192 else 193 client_fd = _dbus_accept (listen_fd); 194 195 if (client_fd < 0) 196 { 197 /* EINTR handled for us */ 198 199 if (_dbus_get_is_errno_eagain_or_ewouldblock ()) 200 _dbus_verbose ("No client available to accept after all\n"); 201 else 202 _dbus_verbose ("Failed to accept a client connection: %s\n", 203 _dbus_strerror_from_errno ()); 204 205 SERVER_UNLOCK (server); 206 } 207 else 208 { 209 if (!handle_new_client_fd_and_unlock (server, client_fd)) 210 _dbus_verbose ("Rejected client connection due to lack of memory\n"); 211 } 212 } 213 214 if (flags & DBUS_WATCH_ERROR) 215 _dbus_verbose ("Error on server listening socket\n"); 216 217 if (flags & DBUS_WATCH_HANGUP) 218 _dbus_verbose ("Hangup on server listening socket\n"); 219 220 return TRUE; 221} 222 223static void 224socket_disconnect (DBusServer *server) 225{ 226 DBusServerSocket *socket_server = (DBusServerSocket*) server; 227 int i; 228 229 HAVE_LOCK_CHECK (server); 230 231 for (i = 0 ; i < socket_server->n_fds ; i++) 232 { 233 if (socket_server->watch[i]) 234 { 235 _dbus_server_remove_watch (server, 236 socket_server->watch[i]); 237 _dbus_watch_invalidate (socket_server->watch[i]); 238 _dbus_watch_unref (socket_server->watch[i]); 239 socket_server->watch[i] = NULL; 240 } 241 242 _dbus_close_socket (socket_server->fds[i], NULL); 243 socket_server->fds[i] = -1; 244 } 245 246 if (socket_server->socket_name != NULL) 247 { 248 DBusString tmp; 249 _dbus_string_init_const (&tmp, socket_server->socket_name); 250 _dbus_delete_file (&tmp, NULL); 251 } 252 253 if (server->published_address) 254 _dbus_daemon_unpublish_session_bus_address(); 255 256 HAVE_LOCK_CHECK (server); 257} 258 259static const DBusServerVTable socket_vtable = { 260 socket_finalize, 261 socket_disconnect 262}; 263 264/** 265 * Creates a new server listening on the given file descriptor. The 266 * file descriptor should be nonblocking (use 267 * _dbus_set_fd_nonblocking() to make it so). The file descriptor 268 * should be listening for connections, that is, listen() should have 269 * been successfully invoked on it. The server will use accept() to 270 * accept new client connections. 271 * 272 * @param fds list of file descriptors. 273 * @param n_fds number of file descriptors 274 * @param address the server's address 275 * @param noncefile to be used for authentication (NULL if not needed) 276 * @returns the new server, or #NULL if no memory. 277 * 278 */ 279DBusServer* 280_dbus_server_new_for_socket (int *fds, 281 int n_fds, 282 const DBusString *address, 283 DBusNonceFile *noncefile) 284{ 285 DBusServerSocket *socket_server; 286 DBusServer *server; 287 int i; 288 289 socket_server = dbus_new0 (DBusServerSocket, 1); 290 if (socket_server == NULL) 291 return NULL; 292 293 socket_server->noncefile = noncefile; 294 295 socket_server->fds = dbus_new (int, n_fds); 296 if (!socket_server->fds) 297 goto failed_0; 298 299 socket_server->watch = dbus_new0 (DBusWatch *, n_fds); 300 if (!socket_server->watch) 301 goto failed_1; 302 303 for (i = 0 ; i < n_fds ; i++) 304 { 305 DBusWatch *watch; 306 307 watch = _dbus_watch_new (fds[i], 308 DBUS_WATCH_READABLE, 309 TRUE, 310 socket_handle_watch, socket_server, 311 NULL); 312 if (watch == NULL) 313 goto failed_2; 314 315 socket_server->n_fds++; 316 socket_server->fds[i] = fds[i]; 317 socket_server->watch[i] = watch; 318 } 319 320 if (!_dbus_server_init_base (&socket_server->base, 321 &socket_vtable, address)) 322 goto failed_2; 323 324 server = (DBusServer*)socket_server; 325 326 SERVER_LOCK (server); 327 328 for (i = 0 ; i < n_fds ; i++) 329 { 330 if (!_dbus_server_add_watch (&socket_server->base, 331 socket_server->watch[i])) 332 { 333 int j; 334 for (j = 0 ; j < i ; j++) 335 _dbus_server_remove_watch (server, 336 socket_server->watch[j]); 337 338 SERVER_UNLOCK (server); 339 _dbus_server_finalize_base (&socket_server->base); 340 goto failed_2; 341 } 342 } 343 344 SERVER_UNLOCK (server); 345 346 _dbus_server_trace_ref (&socket_server->base, 0, 1, "new_for_socket"); 347 return (DBusServer*) socket_server; 348 349 failed_2: 350 for (i = 0 ; i < n_fds ; i++) 351 { 352 if (socket_server->watch[i] != NULL) 353 { 354 _dbus_watch_unref (socket_server->watch[i]); 355 socket_server->watch[i] = NULL; 356 } 357 } 358 dbus_free (socket_server->watch); 359 360 failed_1: 361 dbus_free (socket_server->fds); 362 363 failed_0: 364 dbus_free (socket_server); 365 return NULL; 366} 367 368/** 369 * Creates a new server listening on TCP. 370 * If host is NULL, it will default to localhost. 371 * If bind is NULL, it will default to the value for the host 372 * parameter, and if that is NULL, then localhost 373 * If bind is a hostname, it will be resolved and will listen 374 * on all returned addresses. 375 * If family is NULL, hostname resolution will try all address 376 * families, otherwise it can be ipv4 or ipv6 to restrict the 377 * addresses considered. 378 * 379 * @param host the hostname to report for the listen address 380 * @param bind the hostname to listen on 381 * @param port the port to listen on or 0 to let the OS choose 382 * @param family 383 * @param error location to store reason for failure. 384 * @param use_nonce whether to use a nonce for low-level authentication (nonce-tcp transport) or not (tcp transport) 385 * @returns the new server, or #NULL on failure. 386 */ 387DBusServer* 388_dbus_server_new_for_tcp_socket (const char *host, 389 const char *bind, 390 const char *port, 391 const char *family, 392 DBusError *error, 393 dbus_bool_t use_nonce) 394{ 395 DBusServer *server; 396 int *listen_fds = NULL; 397 int nlisten_fds = 0, i; 398 DBusString address; 399 DBusString host_str; 400 DBusString port_str; 401 DBusNonceFile *noncefile; 402 403 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 404 405 noncefile = NULL; 406 407 if (!_dbus_string_init (&address)) 408 { 409 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 410 return NULL; 411 } 412 413 if (!_dbus_string_init (&port_str)) 414 { 415 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 416 goto failed_0; 417 } 418 419 if (host == NULL) 420 host = "localhost"; 421 422 if (port == NULL) 423 port = "0"; 424 425 if (bind == NULL) 426 bind = host; 427 else if (strcmp (bind, "*") == 0) 428 bind = NULL; 429 430 nlisten_fds =_dbus_listen_tcp_socket (bind, port, family, 431 &port_str, 432 &listen_fds, error); 433 if (nlisten_fds <= 0) 434 { 435 _DBUS_ASSERT_ERROR_IS_SET(error); 436 goto failed_1; 437 } 438 439 _dbus_string_init_const (&host_str, host); 440 if (!_dbus_string_append (&address, use_nonce ? "nonce-tcp:host=" : "tcp:host=") || 441 !_dbus_address_append_escaped (&address, &host_str) || 442 !_dbus_string_append (&address, ",port=") || 443 !_dbus_string_append (&address, _dbus_string_get_const_data(&port_str))) 444 { 445 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 446 goto failed_2; 447 } 448 if (family && 449 (!_dbus_string_append (&address, ",family=") || 450 !_dbus_string_append (&address, family))) 451 { 452 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 453 goto failed_2; 454 } 455 456 if (use_nonce) 457 { 458 noncefile = dbus_new0 (DBusNonceFile, 1); 459 if (noncefile == NULL) 460 { 461 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 462 goto failed_2; 463 } 464 465 if (!_dbus_noncefile_create (noncefile, error)) 466 goto failed_3; 467 468 if (!_dbus_string_append (&address, ",noncefile=") || 469 !_dbus_address_append_escaped (&address, _dbus_noncefile_get_path (noncefile))) 470 { 471 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 472 goto failed_4; 473 } 474 475 } 476 477 server = _dbus_server_new_for_socket (listen_fds, nlisten_fds, &address, noncefile); 478 if (server == NULL) 479 { 480 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 481 goto failed_4; 482 } 483 484 _dbus_string_free (&port_str); 485 _dbus_string_free (&address); 486 dbus_free(listen_fds); 487 488 return server; 489 490 failed_4: 491 _dbus_noncefile_delete (noncefile, NULL); 492 493 failed_3: 494 dbus_free (noncefile); 495 496 failed_2: 497 for (i = 0 ; i < nlisten_fds ; i++) 498 _dbus_close_socket (listen_fds[i], NULL); 499 dbus_free(listen_fds); 500 501 failed_1: 502 _dbus_string_free (&port_str); 503 504 failed_0: 505 _dbus_string_free (&address); 506 507 return NULL; 508} 509 510/** 511 * Tries to interpret the address entry for various socket-related 512 * addresses (well, currently only tcp and nonce-tcp). 513 * 514 * Sets error if the result is not OK. 515 * 516 * @param entry an address entry 517 * @param server_p a new DBusServer, or #NULL on failure. 518 * @param error location to store rationale for failure on bad address 519 * @returns the outcome 520 * 521 */ 522DBusServerListenResult 523_dbus_server_listen_socket (DBusAddressEntry *entry, 524 DBusServer **server_p, 525 DBusError *error) 526{ 527 const char *method; 528 529 *server_p = NULL; 530 531 method = dbus_address_entry_get_method (entry); 532 533 if (strcmp (method, "tcp") == 0 || strcmp (method, "nonce-tcp") == 0) 534 { 535 const char *host; 536 const char *port; 537 const char *bind; 538 const char *family; 539 540 host = dbus_address_entry_get_value (entry, "host"); 541 bind = dbus_address_entry_get_value (entry, "bind"); 542 port = dbus_address_entry_get_value (entry, "port"); 543 family = dbus_address_entry_get_value (entry, "family"); 544 545 *server_p = _dbus_server_new_for_tcp_socket (host, bind, port, 546 family, error, strcmp (method, "nonce-tcp") == 0 ? TRUE : FALSE); 547 548 if (*server_p) 549 { 550 _DBUS_ASSERT_ERROR_IS_CLEAR(error); 551 return DBUS_SERVER_LISTEN_OK; 552 } 553 else 554 { 555 _DBUS_ASSERT_ERROR_IS_SET(error); 556 return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; 557 } 558 } 559 else 560 { 561 _DBUS_ASSERT_ERROR_IS_CLEAR(error); 562 return DBUS_SERVER_LISTEN_NOT_HANDLED; 563 } 564} 565 566/** 567 * This is a bad hack since it's really unix domain socket 568 * specific. Also, the function weirdly adopts ownership 569 * of the passed-in string. 570 * 571 * @param server a socket server 572 * @param filename socket filename to report/delete 573 * 574 */ 575void 576_dbus_server_socket_own_filename (DBusServer *server, 577 char *filename) 578{ 579 DBusServerSocket *socket_server = (DBusServerSocket*) server; 580 581 socket_server->socket_name = filename; 582} 583 584 585/** @} */ 586 587