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