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 |
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 (str1: method_name, str2: "HelloWorld" ) == 0) |
102 | { |
103 | const gchar *greeting; |
104 | gchar *response; |
105 | |
106 | g_variant_get (value: parameters, format_string: "(&s)" , &greeting); |
107 | response = g_strdup_printf (format: "You said '%s'. KTHXBYE!" , greeting); |
108 | g_dbus_method_invocation_return_value (invocation, |
109 | parameters: g_variant_new (format_string: "(s)" , response)); |
110 | g_free (mem: response); |
111 | g_print (format: "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 |
125 | connection_closed (GDBusConnection *connection, |
126 | gboolean remote_peer_vanished, |
127 | GError *Error, |
128 | gpointer user_data) |
129 | { |
130 | g_print (format: "Client disconnected.\n" ); |
131 | g_object_unref (object: connection); |
132 | } |
133 | |
134 | static gboolean |
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 (str: "(no credentials received)" ); |
146 | else |
147 | s = g_credentials_to_string (credentials); |
148 | |
149 | |
150 | g_print (format: "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 | object_path: "/org/gtk/GDBus/TestObject" , |
160 | interface_info: introspection_data->interfaces[0], |
161 | vtable: &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 |
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 (format: "Considering whether to accept %s authentication...\n" , mechanism); |
192 | return TRUE; |
193 | } |
194 | |
195 | static gboolean |
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 (format: "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 (format: "Peer's credentials: %s\n" , credentials_string); |
212 | g_free (mem: credentials_string); |
213 | |
214 | own_credentials = g_credentials_new (); |
215 | |
216 | credentials_string = g_credentials_to_string (credentials: own_credentials); |
217 | g_print (format: "Server's credentials: %s\n" , credentials_string); |
218 | g_free (mem: credentials_string); |
219 | |
220 | if (g_credentials_is_same_user (credentials, other_credentials: own_credentials, NULL)) |
221 | authorized = TRUE; |
222 | |
223 | g_object_unref (object: 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 (format: "A server would often not want to authorize this identity\n" ); |
231 | g_print (format: "Authorizing it anyway for demonstration purposes\n" ); |
232 | authorized = TRUE; |
233 | } |
234 | |
235 | return authorized; |
236 | } |
237 | |
238 | /* ---------------------------------------------------------------------------------------------------- */ |
239 | |
240 | int |
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 (parameter_string: "peer-to-peer example" ); |
264 | error = NULL; |
265 | g_option_context_add_main_entries (context: opt_context, entries: opt_entries, NULL); |
266 | if (!g_option_context_parse (context: opt_context, argc: &argc, argv: &argv, error: &error)) |
267 | { |
268 | g_printerr (format: "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 (format: "Incorrect usage, try --help.\n" ); |
275 | goto out; |
276 | } |
277 | if (!opt_server && opt_allow_anonymous) |
278 | { |
279 | g_printerr (format: "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 (xml_data: 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 (address: opt_address, |
310 | flags: server_flags, |
311 | guid, |
312 | observer, |
313 | NULL, /* GCancellable */ |
314 | error: &error); |
315 | g_dbus_server_start (server); |
316 | |
317 | g_object_unref (object: observer); |
318 | g_free (mem: guid); |
319 | |
320 | if (server == NULL) |
321 | { |
322 | g_printerr (format: "Error creating server at address %s: %s\n" , opt_address, error->message); |
323 | g_error_free (error); |
324 | goto out; |
325 | } |
326 | g_print (format: "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 (object: 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 (address: opt_address, |
347 | flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, |
348 | NULL, /* GDBusAuthObserver */ |
349 | NULL, /* GCancellable */ |
350 | error: &error); |
351 | if (connection == NULL) |
352 | { |
353 | g_printerr (format: "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 (format: "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 (format: "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 | object_path: "/org/gtk/GDBus/TestObject" , |
367 | interface_name: "org.gtk.GDBus.TestPeerInterface" , |
368 | method_name: "HelloWorld" , |
369 | parameters: g_variant_new (format_string: "(s)" , greeting), |
370 | G_VARIANT_TYPE ("(s)" ), |
371 | flags: G_DBUS_CALL_FLAGS_NONE, |
372 | timeout_msec: -1, |
373 | NULL, |
374 | error: &error); |
375 | if (value == NULL) |
376 | { |
377 | g_printerr (format: "Error invoking HelloWorld(): %s\n" , error->message); |
378 | g_error_free (error); |
379 | goto out; |
380 | } |
381 | g_variant_get (value, format_string: "(&s)" , &greeting_response); |
382 | g_print (format: "Server said: %s\n" , greeting_response); |
383 | g_variant_unref (value); |
384 | |
385 | g_object_unref (object: connection); |
386 | } |
387 | g_dbus_node_info_unref (info: introspection_data); |
388 | |
389 | ret = 0; |
390 | |
391 | out: |
392 | return ret; |
393 | } |
394 | |