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. expf(mut x: f32) -> f3234 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 (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 { 0 } 121 }; 122 123 /* ---------------------------------------------------------------------------------------------------- */ 124 125 static void 126 connection_closed (GDBusConnection *connection, 127 gboolean remote_peer_vanished, 128 GError *Error, 129 gpointer user_data) 130 { 131 g_print ("Client disconnected.\n"); 132 g_object_unref (connection); 133 } 134 135 static gboolean 136 on_new_connection (GDBusServer *server, 137 GDBusConnection *connection, 138 gpointer user_data) 139 { 140 guint registration_id; 141 GCredentials *credentials; 142 gchar *s; 143 144 credentials = g_dbus_connection_get_peer_credentials (connection); 145 if (credentials == NULL) 146 s = g_strdup ("(no credentials received)"); 147 else 148 s = g_credentials_to_string (credentials); 149 150 151 g_print ("Client connected.\n" 152 "Peer credentials: %s\n" 153 "Negotiated capabilities: unix-fd-passing=%d\n", 154 s, 155 g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); 156 157 g_object_ref (connection); 158 g_signal_connect (connection, "closed", G_CALLBACK (connection_closed), NULL); 159 registration_id = g_dbus_connection_register_object (connection, 160 "/org/gtk/GDBus/TestObject", 161 introspection_data->interfaces[0], 162 &interface_vtable, 163 NULL, /* user_data */ 164 NULL, /* user_data_free_func */ 165 NULL); /* GError** */ 166 g_assert (registration_id > 0); 167 168 return TRUE; 169 } 170 171 /* ---------------------------------------------------------------------------------------------------- */ 172 173 static gboolean 174 allow_mechanism_cb (GDBusAuthObserver *observer, 175 const gchar *mechanism, 176 G_GNUC_UNUSED gpointer user_data) 177 { 178 /* 179 * In a production GDBusServer that only needs to work on modern Unix 180 * platforms, consider requiring EXTERNAL (credentials-passing), 181 * which is the recommended authentication mechanism for AF_UNIX 182 * sockets: 183 * 184 * if (g_strcmp0 (mechanism, "EXTERNAL") == 0) 185 * return TRUE; 186 * 187 * return FALSE; 188 * 189 * For this example we accept everything. 190 */ 191 192 g_print ("Considering whether to accept %s authentication...\n", mechanism); 193 return TRUE; 194 } 195 196 static gboolean 197 authorize_authenticated_peer_cb (GDBusAuthObserver *observer, 198 G_GNUC_UNUSED GIOStream *stream, 199 GCredentials *credentials, 200 G_GNUC_UNUSED gpointer user_data) 201 { 202 gboolean authorized = FALSE; 203 204 g_print ("Considering whether to authorize authenticated peer...\n"); 205 206 if (credentials != NULL) 207 { 208 GCredentials *own_credentials; 209 gchar *credentials_string = NULL; 210 211 credentials_string = g_credentials_to_string (credentials); 212 g_print ("Peer's credentials: %s\n", credentials_string); 213 g_free (credentials_string); 214 215 own_credentials = g_credentials_new (); 216 217 credentials_string = g_credentials_to_string (own_credentials); 218 g_print ("Server's credentials: %s\n", credentials_string); 219 g_free (credentials_string); 220 221 if (g_credentials_is_same_user (credentials, own_credentials, NULL)) 222 authorized = TRUE; 223 224 g_object_unref (own_credentials); 225 } 226 227 if (!authorized) 228 { 229 /* In most servers you'd want to reject this, but for this example 230 * we allow it. */ 231 g_print ("A server would often not want to authorize this identity\n"); 232 g_print ("Authorizing it anyway for demonstration purposes\n"); 233 authorized = TRUE; 234 } 235 236 return authorized; 237 } 238 239 /* ---------------------------------------------------------------------------------------------------- */ 240 241 int 242 main (int argc, char *argv[]) 243 { 244 gint ret; 245 gboolean opt_server; 246 gchar *opt_address; 247 GOptionContext *opt_context; 248 gboolean opt_allow_anonymous; 249 GError *error; 250 GOptionEntry opt_entries[] = 251 { 252 { "server", 's', 0, G_OPTION_ARG_NONE, &opt_server, "Start a server instead of a client", NULL }, 253 { "address", 'a', 0, G_OPTION_ARG_STRING, &opt_address, "D-Bus address to use", NULL }, 254 { "allow-anonymous", 'n', 0, G_OPTION_ARG_NONE, &opt_allow_anonymous, "Allow anonymous authentication", NULL }, 255 G_OPTION_ENTRY_NULL 256 }; 257 258 ret = 1; 259 260 opt_address = NULL; 261 opt_server = FALSE; 262 opt_allow_anonymous = FALSE; 263 264 opt_context = g_option_context_new ("peer-to-peer example"); 265 error = NULL; 266 g_option_context_add_main_entries (opt_context, opt_entries, NULL); 267 if (!g_option_context_parse (opt_context, &argc, &argv, &error)) 268 { 269 g_printerr ("Error parsing options: %s\n", error->message); 270 g_error_free (error); 271 goto out; 272 } 273 if (opt_address == NULL) 274 { 275 g_printerr ("Incorrect usage, try --help.\n"); 276 goto out; 277 } 278 if (!opt_server && opt_allow_anonymous) 279 { 280 g_printerr ("The --allow-anonymous option only makes sense when used with --server.\n"); 281 goto out; 282 } 283 284 /* We are lazy here - we don't want to manually provide 285 * the introspection data structures - so we just build 286 * them from XML. 287 */ 288 introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); 289 g_assert (introspection_data != NULL); 290 291 if (opt_server) 292 { 293 GDBusAuthObserver *observer; 294 GDBusServer *server; 295 gchar *guid; 296 GMainLoop *loop; 297 GDBusServerFlags server_flags; 298 299 guid = g_dbus_generate_guid (); 300 301 server_flags = G_DBUS_SERVER_FLAGS_NONE; 302 if (opt_allow_anonymous) 303 server_flags |= G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; 304 305 observer = g_dbus_auth_observer_new (); 306 g_signal_connect (observer, "allow-mechanism", G_CALLBACK (allow_mechanism_cb), NULL); 307 g_signal_connect (observer, "authorize-authenticated-peer", G_CALLBACK (authorize_authenticated_peer_cb), NULL); 308 309 error = NULL; 310 server = g_dbus_server_new_sync (opt_address, 311 server_flags, 312 guid, 313 observer, 314 NULL, /* GCancellable */ 315 &error); 316 g_dbus_server_start (server); 317 318 g_object_unref (observer); 319 g_free (guid); 320 321 if (server == NULL) 322 { 323 g_printerr ("Error creating server at address %s: %s\n", opt_address, error->message); 324 g_error_free (error); 325 goto out; 326 } 327 g_print ("Server is listening at: %s\n", g_dbus_server_get_client_address (server)); 328 g_signal_connect (server, 329 "new-connection", 330 G_CALLBACK (on_new_connection), 331 NULL); 332 333 loop = g_main_loop_new (NULL, FALSE); 334 g_main_loop_run (loop); 335 336 g_object_unref (server); 337 g_main_loop_unref (loop); 338 } 339 else 340 { 341 GDBusConnection *connection; 342 const gchar *greeting_response; 343 GVariant *value; 344 gchar *greeting; 345 346 error = NULL; 347 connection = g_dbus_connection_new_for_address_sync (opt_address, 348 G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, 349 NULL, /* GDBusAuthObserver */ 350 NULL, /* GCancellable */ 351 &error); 352 if (connection == NULL) 353 { 354 g_printerr ("Error connecting to D-Bus address %s: %s\n", opt_address, error->message); 355 g_error_free (error); 356 goto out; 357 } 358 359 g_print ("Connected.\n" 360 "Negotiated capabilities: unix-fd-passing=%d\n", 361 g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); 362 363 greeting = g_strdup_printf ("Hey, it's %" G_GINT64_FORMAT " already!", 364 g_get_real_time () / G_USEC_PER_SEC); 365 value = g_dbus_connection_call_sync (connection, 366 NULL, /* bus_name */ 367 "/org/gtk/GDBus/TestObject", 368 "org.gtk.GDBus.TestPeerInterface", 369 "HelloWorld", 370 g_variant_new ("(s)", greeting), 371 G_VARIANT_TYPE ("(s)"), 372 G_DBUS_CALL_FLAGS_NONE, 373 -1, 374 NULL, 375 &error); 376 if (value == NULL) 377 { 378 g_printerr ("Error invoking HelloWorld(): %s\n", error->message); 379 g_error_free (error); 380 goto out; 381 } 382 g_variant_get (value, "(&s)", &greeting_response); 383 g_print ("Server said: %s\n", greeting_response); 384 g_variant_unref (value); 385 386 g_object_unref (connection); 387 } 388 g_dbus_node_info_unref (introspection_data); 389 390 ret = 0; 391 392 out: 393 return ret; 394 } 395