1 /*
2
3 Usage examples (modulo addresses / credentials).
4
5 UNIX domain socket transport:
6
7 Server:
8 $ ./gdbus-example-peer --server --address unix:abstract=myaddr
9 Server is listening at: unix:abstract=myaddr
10 Client connected.
11 Peer credentials: GCredentials:unix-user=500,unix-group=500,unix-process=13378
12 Negotiated capabilities: unix-fd-passing=1
13 Client said: Hey, it's 1273093080 already!
14
15 Client:
16 $ ./gdbus-example-peer --address unix:abstract=myaddr
17 Connected.
18 Negotiated capabilities: unix-fd-passing=1
19 Server said: You said 'Hey, it's 1273093080 already!'. KTHXBYE!
20
21 Nonce-secured TCP transport on the same host:
22
23 Server:
24 $ ./gdbus-example-peer --server --address nonce-tcp:
25 Server is listening at: nonce-tcp:host=localhost,port=43077,noncefile=/tmp/gdbus-nonce-file-X1ZNCV
26 Client connected.
27 Peer credentials: (no credentials received)
28 Negotiated capabilities: unix-fd-passing=0
29 Client said: Hey, it's 1273093206 already!
30
31 Client:
32 $ ./gdbus-example-peer -address nonce-tcp:host=localhost,port=43077,noncefile=/tmp/gdbus-nonce-file-X1ZNCV
33 Connected.
34 Negotiated capabilities: unix-fd-passing=0
35 Server said: You said 'Hey, it's 1273093206 already!'. KTHXBYE!
36
37 TCP transport on two different hosts with a shared home directory:
38
39 Server:
40 host1 $ ./gdbus-example-peer --server --address tcp:host=0.0.0.0
41 Server is listening at: tcp:host=0.0.0.0,port=46314
42 Client connected.
43 Peer credentials: (no credentials received)
44 Negotiated capabilities: unix-fd-passing=0
45 Client said: Hey, it's 1273093337 already!
46
47 Client:
48 host2 $ ./gdbus-example-peer -a tcp:host=host1,port=46314
49 Connected.
50 Negotiated capabilities: unix-fd-passing=0
51 Server said: You said 'Hey, it's 1273093337 already!'. KTHXBYE!
52
53 TCP transport on two different hosts without authentication:
54
55 Server:
56 host1 $ ./gdbus-example-peer --server --address tcp:host=0.0.0.0 --allow-anonymous
57 Server is listening at: tcp:host=0.0.0.0,port=59556
58 Client connected.
59 Peer credentials: (no credentials received)
60 Negotiated capabilities: unix-fd-passing=0
61 Client said: Hey, it's 1273093652 already!
62
63 Client:
64 host2 $ ./gdbus-example-peer -a tcp:host=host1,port=59556
65 Connected.
66 Negotiated capabilities: unix-fd-passing=0
67 Server said: You said 'Hey, it's 1273093652 already!'. KTHXBYE!
68
69 */
70
71 #include <gio/gio.h>
72 #include <stdlib.h>
73
74 /* ---------------------------------------------------------------------------------------------------- */
75
76 static GDBusNodeInfo *introspection_data = NULL;
77
78 /* Introspection data for the service we are exporting */
79 static const gchar introspection_xml[] =
80 "<node>"
81 " <interface name='org.gtk.GDBus.TestPeerInterface'>"
82 " <method name='HelloWorld'>"
83 " <arg type='s' name='greeting' direction='in'/>"
84 " <arg type='s' name='response' direction='out'/>"
85 " </method>"
86 " </interface>"
87 "</node>";
88
89 /* ---------------------------------------------------------------------------------------------------- */
90
91 static void
handle_method_call(GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * method_name,GVariant * parameters,GDBusMethodInvocation * invocation,gpointer user_data)92 handle_method_call (GDBusConnection *connection,
93 const gchar *sender,
94 const gchar *object_path,
95 const gchar *interface_name,
96 const gchar *method_name,
97 GVariant *parameters,
98 GDBusMethodInvocation *invocation,
99 gpointer user_data)
100 {
101 if (g_strcmp0 (method_name, "HelloWorld") == 0)
102 {
103 const gchar *greeting;
104 gchar *response;
105
106 g_variant_get (parameters, "(&s)", &greeting);
107 response = g_strdup_printf ("You said '%s'. KTHXBYE!", greeting);
108 g_dbus_method_invocation_return_value (invocation,
109 g_variant_new ("(s)", response));
110 g_free (response);
111 g_print ("Client said: %s\n", greeting);
112 }
113 }
114
115 static const GDBusInterfaceVTable interface_vtable =
116 {
117 handle_method_call,
118 NULL,
119 NULL,
120 };
121
122 /* ---------------------------------------------------------------------------------------------------- */
123
124 static void
connection_closed(GDBusConnection * connection,gboolean remote_peer_vanished,GError * Error,gpointer user_data)125 connection_closed (GDBusConnection *connection,
126 gboolean remote_peer_vanished,
127 GError *Error,
128 gpointer user_data)
129 {
130 g_print ("Client disconnected.\n");
131 g_object_unref (connection);
132 }
133
134 static gboolean
on_new_connection(GDBusServer * server,GDBusConnection * connection,gpointer user_data)135 on_new_connection (GDBusServer *server,
136 GDBusConnection *connection,
137 gpointer user_data)
138 {
139 guint registration_id;
140 GCredentials *credentials;
141 gchar *s;
142
143 credentials = g_dbus_connection_get_peer_credentials (connection);
144 if (credentials == NULL)
145 s = g_strdup ("(no credentials received)");
146 else
147 s = g_credentials_to_string (credentials);
148
149
150 g_print ("Client connected.\n"
151 "Peer credentials: %s\n"
152 "Negotiated capabilities: unix-fd-passing=%d\n",
153 s,
154 g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING);
155
156 g_object_ref (connection);
157 g_signal_connect (connection, "closed", G_CALLBACK (connection_closed), NULL);
158 registration_id = g_dbus_connection_register_object (connection,
159 "/org/gtk/GDBus/TestObject",
160 introspection_data->interfaces[0],
161 &interface_vtable,
162 NULL, /* user_data */
163 NULL, /* user_data_free_func */
164 NULL); /* GError** */
165 g_assert (registration_id > 0);
166
167 return TRUE;
168 }
169
170 /* ---------------------------------------------------------------------------------------------------- */
171
172 static gboolean
allow_mechanism_cb(GDBusAuthObserver * observer,const gchar * mechanism,G_GNUC_UNUSED gpointer user_data)173 allow_mechanism_cb (GDBusAuthObserver *observer,
174 const gchar *mechanism,
175 G_GNUC_UNUSED gpointer user_data)
176 {
177 /*
178 * In a production GDBusServer that only needs to work on modern Unix
179 * platforms, consider requiring EXTERNAL (credentials-passing),
180 * which is the recommended authentication mechanism for AF_UNIX
181 * sockets:
182 *
183 * if (g_strcmp0 (mechanism, "EXTERNAL") == 0)
184 * return TRUE;
185 *
186 * return FALSE;
187 *
188 * For this example we accept everything.
189 */
190
191 g_print ("Considering whether to accept %s authentication...\n", mechanism);
192 return TRUE;
193 }
194
195 static gboolean
authorize_authenticated_peer_cb(GDBusAuthObserver * observer,G_GNUC_UNUSED GIOStream * stream,GCredentials * credentials,G_GNUC_UNUSED gpointer user_data)196 authorize_authenticated_peer_cb (GDBusAuthObserver *observer,
197 G_GNUC_UNUSED GIOStream *stream,
198 GCredentials *credentials,
199 G_GNUC_UNUSED gpointer user_data)
200 {
201 gboolean authorized = FALSE;
202
203 g_print ("Considering whether to authorize authenticated peer...\n");
204
205 if (credentials != NULL)
206 {
207 GCredentials *own_credentials;
208 gchar *credentials_string = NULL;
209
210 credentials_string = g_credentials_to_string (credentials);
211 g_print ("Peer's credentials: %s\n", credentials_string);
212 g_free (credentials_string);
213
214 own_credentials = g_credentials_new ();
215
216 credentials_string = g_credentials_to_string (own_credentials);
217 g_print ("Server's credentials: %s\n", credentials_string);
218 g_free (credentials_string);
219
220 if (g_credentials_is_same_user (credentials, own_credentials, NULL))
221 authorized = TRUE;
222
223 g_object_unref (own_credentials);
224 }
225
226 if (!authorized)
227 {
228 /* In most servers you'd want to reject this, but for this example
229 * we allow it. */
230 g_print ("A server would often not want to authorize this identity\n");
231 g_print ("Authorizing it anyway for demonstration purposes\n");
232 authorized = TRUE;
233 }
234
235 return authorized;
236 }
237
238 /* ---------------------------------------------------------------------------------------------------- */
239
240 int
main(int argc,char * argv[])241 main (int argc, char *argv[])
242 {
243 gint ret;
244 gboolean opt_server;
245 gchar *opt_address;
246 GOptionContext *opt_context;
247 gboolean opt_allow_anonymous;
248 GError *error;
249 GOptionEntry opt_entries[] =
250 {
251 { "server", 's', 0, G_OPTION_ARG_NONE, &opt_server, "Start a server instead of a client", NULL },
252 { "address", 'a', 0, G_OPTION_ARG_STRING, &opt_address, "D-Bus address to use", NULL },
253 { "allow-anonymous", 'n', 0, G_OPTION_ARG_NONE, &opt_allow_anonymous, "Allow anonymous authentication", NULL },
254 { NULL}
255 };
256
257 ret = 1;
258
259 opt_address = NULL;
260 opt_server = FALSE;
261 opt_allow_anonymous = FALSE;
262
263 opt_context = g_option_context_new ("peer-to-peer example");
264 error = NULL;
265 g_option_context_add_main_entries (opt_context, opt_entries, NULL);
266 if (!g_option_context_parse (opt_context, &argc, &argv, &error))
267 {
268 g_printerr ("Error parsing options: %s\n", error->message);
269 g_error_free (error);
270 goto out;
271 }
272 if (opt_address == NULL)
273 {
274 g_printerr ("Incorrect usage, try --help.\n");
275 goto out;
276 }
277 if (!opt_server && opt_allow_anonymous)
278 {
279 g_printerr ("The --allow-anonymous option only makes sense when used with --server.\n");
280 goto out;
281 }
282
283 /* We are lazy here - we don't want to manually provide
284 * the introspection data structures - so we just build
285 * them from XML.
286 */
287 introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
288 g_assert (introspection_data != NULL);
289
290 if (opt_server)
291 {
292 GDBusAuthObserver *observer;
293 GDBusServer *server;
294 gchar *guid;
295 GMainLoop *loop;
296 GDBusServerFlags server_flags;
297
298 guid = g_dbus_generate_guid ();
299
300 server_flags = G_DBUS_SERVER_FLAGS_NONE;
301 if (opt_allow_anonymous)
302 server_flags |= G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS;
303
304 observer = g_dbus_auth_observer_new ();
305 g_signal_connect (observer, "allow-mechanism", G_CALLBACK (allow_mechanism_cb), NULL);
306 g_signal_connect (observer, "authorize-authenticated-peer", G_CALLBACK (authorize_authenticated_peer_cb), NULL);
307
308 error = NULL;
309 server = g_dbus_server_new_sync (opt_address,
310 server_flags,
311 guid,
312 observer,
313 NULL, /* GCancellable */
314 &error);
315 g_dbus_server_start (server);
316
317 g_object_unref (observer);
318 g_free (guid);
319
320 if (server == NULL)
321 {
322 g_printerr ("Error creating server at address %s: %s\n", opt_address, error->message);
323 g_error_free (error);
324 goto out;
325 }
326 g_print ("Server is listening at: %s\n", g_dbus_server_get_client_address (server));
327 g_signal_connect (server,
328 "new-connection",
329 G_CALLBACK (on_new_connection),
330 NULL);
331
332 loop = g_main_loop_new (NULL, FALSE);
333 g_main_loop_run (loop);
334
335 g_object_unref (server);
336 g_main_loop_unref (loop);
337 }
338 else
339 {
340 GDBusConnection *connection;
341 const gchar *greeting_response;
342 GVariant *value;
343 gchar *greeting;
344
345 error = NULL;
346 connection = g_dbus_connection_new_for_address_sync (opt_address,
347 G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
348 NULL, /* GDBusAuthObserver */
349 NULL, /* GCancellable */
350 &error);
351 if (connection == NULL)
352 {
353 g_printerr ("Error connecting to D-Bus address %s: %s\n", opt_address, error->message);
354 g_error_free (error);
355 goto out;
356 }
357
358 g_print ("Connected.\n"
359 "Negotiated capabilities: unix-fd-passing=%d\n",
360 g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING);
361
362 greeting = g_strdup_printf ("Hey, it's %" G_GINT64_FORMAT " already!",
363 g_get_real_time () / G_USEC_PER_SEC);
364 value = g_dbus_connection_call_sync (connection,
365 NULL, /* bus_name */
366 "/org/gtk/GDBus/TestObject",
367 "org.gtk.GDBus.TestPeerInterface",
368 "HelloWorld",
369 g_variant_new ("(s)", greeting),
370 G_VARIANT_TYPE ("(s)"),
371 G_DBUS_CALL_FLAGS_NONE,
372 -1,
373 NULL,
374 &error);
375 if (value == NULL)
376 {
377 g_printerr ("Error invoking HelloWorld(): %s\n", error->message);
378 g_error_free (error);
379 goto out;
380 }
381 g_variant_get (value, "(&s)", &greeting_response);
382 g_print ("Server said: %s\n", greeting_response);
383 g_variant_unref (value);
384
385 g_object_unref (connection);
386 }
387 g_dbus_node_info_unref (introspection_data);
388
389 ret = 0;
390
391 out:
392 return ret;
393 }
394