[kdbus] Fix problem with receiving async messages
[platform/upstream/glib.git] / gio / tests / resolver.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /* GIO - GLib Input, Output and Streaming Library
4  * 
5  * Copyright (C) 2008 Red Hat, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General
18  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "config.h"
22 #include <glib.h>
23 #include "glibintl.h"
24
25 #include <signal.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #ifdef G_OS_UNIX
30 #include <unistd.h>
31 #endif
32
33 #include <gio/gio.h>
34
35 static GResolver *resolver;
36 static GCancellable *cancellable;
37 static GMainLoop *loop;
38 static int nlookups = 0;
39 static gboolean synchronous = FALSE;
40 static guint connectable_count = 0;
41 static GResolverRecordType record_type = 0;
42
43 static void G_GNUC_NORETURN
44 usage (void)
45 {
46         fprintf (stderr, "Usage: resolver [-s] [hostname | IP | service/protocol/domain ] ...\n");
47         fprintf (stderr, "Usage: resolver [-s] [-t MX|TXT|NS|SOA] rrname ...\n");
48         fprintf (stderr, "       resolver [-s] -c NUMBER [hostname | IP | service/protocol/domain ]\n");
49         fprintf (stderr, "       Use -s to do synchronous lookups.\n");
50         fprintf (stderr, "       Use -c NUMBER (and only a single resolvable argument) to test GSocketConnectable.\n");
51         fprintf (stderr, "       The given NUMBER determines how many times the connectable will be enumerated.\n");
52         fprintf (stderr, "       Use -t with MX, TXT, NS or SOA to lookup DNS records of those types.\n");
53         exit (1);
54 }
55
56 G_LOCK_DEFINE_STATIC (response);
57
58 static void
59 done_lookup (void)
60 {
61   nlookups--;
62   if (nlookups == 0)
63     {
64       /* In the sync case we need to make sure we don't call
65        * g_main_loop_quit before the loop is actually running...
66        */
67       g_idle_add ((GSourceFunc)g_main_loop_quit, loop);
68     }
69 }
70
71 static void
72 print_resolved_name (const char *phys,
73                      char       *name,
74                      GError     *error)
75 {
76   G_LOCK (response);
77   printf ("Address: %s\n", phys);
78   if (error)
79     {
80       printf ("Error:   %s\n", error->message);
81       g_error_free (error);
82     }
83   else
84     {
85       printf ("Name:    %s\n", name);
86       g_free (name);
87     }
88   printf ("\n");
89
90   done_lookup ();
91   G_UNLOCK (response);
92 }
93
94 static void
95 print_resolved_addresses (const char *name,
96                           GList      *addresses,
97                           GError     *error)
98 {
99   char *phys;
100   GList *a;
101
102   G_LOCK (response);
103   printf ("Name:    %s\n", name);
104   if (error)
105     {
106       printf ("Error:   %s\n", error->message);
107       g_error_free (error);
108     }
109   else
110     {
111       for (a = addresses; a; a = a->next)
112         {
113           phys = g_inet_address_to_string (a->data);
114           printf ("Address: %s\n", phys);
115           g_free (phys);
116           g_object_unref (a->data);
117         }
118       g_list_free (addresses);
119     }
120   printf ("\n");
121
122   done_lookup ();
123   G_UNLOCK (response);
124 }
125
126 static void
127 print_resolved_service (const char *service,
128                         GList      *targets,
129                         GError     *error)
130 {
131   GList *t;  
132
133   G_LOCK (response);
134   printf ("Service: %s\n", service);
135   if (error)
136     {
137       printf ("Error: %s\n", error->message);
138       g_error_free (error);
139     }
140   else
141     {
142       for (t = targets; t; t = t->next)
143         {
144           printf ("%s:%u (pri %u, weight %u)\n",
145                   g_srv_target_get_hostname (t->data),
146                   (guint)g_srv_target_get_port (t->data),
147                   (guint)g_srv_target_get_priority (t->data),
148                   (guint)g_srv_target_get_weight (t->data));
149           g_srv_target_free (t->data);
150         }
151       g_list_free (targets);
152     }
153   printf ("\n");
154
155   done_lookup ();
156   G_UNLOCK (response);
157 }
158
159 static void
160 print_resolved_mx (const char *rrname,
161                    GList      *records,
162                    GError     *error)
163 {
164   const gchar *hostname;
165   guint16 priority;
166   GList *t;
167
168   G_LOCK (response);
169   printf ("Domain: %s\n", rrname);
170   if (error)
171     {
172       printf ("Error: %s\n", error->message);
173       g_error_free (error);
174     }
175   else if (!records)
176     {
177       printf ("no MX records\n");
178     }
179   else
180     {
181       for (t = records; t; t = t->next)
182         {
183           g_variant_get (t->data, "(q&s)", &priority, &hostname);
184           printf ("%s (pri %u)\n", hostname, (guint)priority);
185           g_variant_unref (t->data);
186         }
187       g_list_free (records);
188     }
189   printf ("\n");
190
191   done_lookup ();
192   G_UNLOCK (response);
193 }
194
195 static void
196 print_resolved_txt (const char *rrname,
197                     GList      *records,
198                     GError     *error)
199 {
200   const gchar **contents;
201   GList *t;
202   gint i;
203
204   G_LOCK (response);
205   printf ("Domain: %s\n", rrname);
206   if (error)
207     {
208       printf ("Error: %s\n", error->message);
209       g_error_free (error);
210     }
211   else if (!records)
212     {
213       printf ("no TXT records\n");
214     }
215   else
216     {
217       for (t = records; t; t = t->next)
218         {
219           if (t != records)
220             printf ("\n");
221           g_variant_get (t->data, "(^a&s)", &contents);
222           for (i = 0; contents[i] != NULL; i++)
223             printf ("%s\n", contents[i]);
224           g_variant_unref (t->data);
225         }
226       g_list_free (records);
227     }
228   printf ("\n");
229
230   done_lookup ();
231   G_UNLOCK (response);
232 }
233
234 static void
235 print_resolved_soa (const char *rrname,
236                     GList      *records,
237                     GError     *error)
238 {
239   GList *t;
240   const gchar *primary_ns;
241   const gchar *administrator;
242   guint32 serial, refresh, retry, expire, ttl;
243
244   G_LOCK (response);
245   printf ("Zone: %s\n", rrname);
246   if (error)
247     {
248       printf ("Error: %s\n", error->message);
249       g_error_free (error);
250     }
251   else if (!records)
252     {
253       printf ("no SOA records\n");
254     }
255   else
256     {
257       for (t = records; t; t = t->next)
258         {
259           g_variant_get (t->data, "(&s&suuuuu)", &primary_ns, &administrator,
260                          &serial, &refresh, &retry, &expire, &ttl);
261           printf ("%s %s (serial %u, refresh %u, retry %u, expire %u, ttl %u)\n",
262                   primary_ns, administrator, (guint)serial, (guint)refresh,
263                   (guint)retry, (guint)expire, (guint)ttl);
264           g_variant_unref (t->data);
265         }
266       g_list_free (records);
267     }
268   printf ("\n");
269
270   done_lookup ();
271   G_UNLOCK (response);
272 }
273
274 static void
275 print_resolved_ns (const char *rrname,
276                     GList      *records,
277                     GError     *error)
278 {
279   GList *t;
280   const gchar *hostname;
281
282   G_LOCK (response);
283   printf ("Zone: %s\n", rrname);
284   if (error)
285     {
286       printf ("Error: %s\n", error->message);
287       g_error_free (error);
288     }
289   else if (!records)
290     {
291       printf ("no NS records\n");
292     }
293   else
294     {
295       for (t = records; t; t = t->next)
296         {
297           g_variant_get (t->data, "(&s)", &hostname);
298           printf ("%s\n", hostname);
299           g_variant_unref (t->data);
300         }
301       g_list_free (records);
302     }
303   printf ("\n");
304
305   done_lookup ();
306   G_UNLOCK (response);
307 }
308
309 static void
310 lookup_one_sync (const char *arg)
311 {
312   GError *error = NULL;
313
314   if (record_type != 0)
315     {
316       GList *records;
317
318       records = g_resolver_lookup_records (resolver, arg, record_type, cancellable, &error);
319       switch (record_type)
320       {
321         case G_RESOLVER_RECORD_MX:
322           print_resolved_mx (arg, records, error);
323           break;
324         case G_RESOLVER_RECORD_SOA:
325           print_resolved_soa (arg, records, error);
326           break;
327         case G_RESOLVER_RECORD_NS:
328           print_resolved_ns (arg, records, error);
329           break;
330         case G_RESOLVER_RECORD_TXT:
331           print_resolved_txt (arg, records, error);
332           break;
333         default:
334           g_warn_if_reached ();
335           break;
336       }
337     }
338   else if (strchr (arg, '/'))
339     {
340       GList *targets;
341       /* service/protocol/domain */
342       char **parts = g_strsplit (arg, "/", 3);
343
344       if (!parts || !parts[2])
345         usage ();
346
347       targets = g_resolver_lookup_service (resolver,
348                                            parts[0], parts[1], parts[2],
349                                            cancellable, &error);
350       print_resolved_service (arg, targets, error);
351     }
352   else if (g_hostname_is_ip_address (arg))
353     {
354       GInetAddress *addr = g_inet_address_new_from_string (arg);
355       char *name;
356
357       name = g_resolver_lookup_by_address (resolver, addr, cancellable, &error);
358       print_resolved_name (arg, name, error);
359       g_object_unref (addr);
360     }
361   else
362     {
363       GList *addresses;
364
365       addresses = g_resolver_lookup_by_name (resolver, arg, cancellable, &error);
366       print_resolved_addresses (arg, addresses, error);
367     }
368 }
369
370 static gpointer
371 lookup_thread (gpointer arg)
372 {
373   lookup_one_sync (arg);
374   return NULL;
375 }
376
377 static void
378 start_sync_lookups (char **argv, int argc)
379 {
380   int i;
381
382   for (i = 0; i < argc; i++)
383     {
384       GThread *thread;
385       thread = g_thread_new ("lookup", lookup_thread, argv[i]);
386       g_thread_unref (thread);
387     }
388 }
389
390 static void
391 lookup_by_addr_callback (GObject *source, GAsyncResult *result,
392                          gpointer user_data)
393 {
394   const char *phys = user_data;
395   GError *error = NULL;
396   char *name;
397
398   name = g_resolver_lookup_by_address_finish (resolver, result, &error);
399   print_resolved_name (phys, name, error);
400 }
401
402 static void
403 lookup_by_name_callback (GObject *source, GAsyncResult *result,
404                          gpointer user_data)
405 {
406   const char *name = user_data;
407   GError *error = NULL;
408   GList *addresses;
409
410   addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
411   print_resolved_addresses (name, addresses, error);
412 }
413
414 static void
415 lookup_service_callback (GObject *source, GAsyncResult *result,
416                          gpointer user_data)
417 {
418   const char *service = user_data;
419   GError *error = NULL;
420   GList *targets;
421
422   targets = g_resolver_lookup_service_finish (resolver, result, &error);
423   print_resolved_service (service, targets, error);
424 }
425
426 static void
427 lookup_records_callback (GObject      *source,
428                          GAsyncResult *result,
429                          gpointer      user_data)
430 {
431   const char *arg = user_data;
432   GError *error = NULL;
433   GList *records;
434
435   records = g_resolver_lookup_records_finish (resolver, result, &error);
436
437   switch (record_type)
438   {
439     case G_RESOLVER_RECORD_MX:
440       print_resolved_mx (arg, records, error);
441       break;
442     case G_RESOLVER_RECORD_SOA:
443       print_resolved_soa (arg, records, error);
444       break;
445     case G_RESOLVER_RECORD_NS:
446       print_resolved_ns (arg, records, error);
447       break;
448     case G_RESOLVER_RECORD_TXT:
449       print_resolved_txt (arg, records, error);
450       break;
451     default:
452       g_warn_if_reached ();
453       break;
454   }
455 }
456
457 static void
458 start_async_lookups (char **argv, int argc)
459 {
460   int i;
461
462   for (i = 0; i < argc; i++)
463     {
464       if (record_type != 0)
465         {
466           g_resolver_lookup_records_async (resolver, argv[i], record_type,
467                                            cancellable, lookup_records_callback, argv[i]);
468         }
469       else if (strchr (argv[i], '/'))
470         {
471           /* service/protocol/domain */
472           char **parts = g_strsplit (argv[i], "/", 3);
473
474           if (!parts || !parts[2])
475             usage ();
476
477           g_resolver_lookup_service_async (resolver,
478                                            parts[0], parts[1], parts[2],
479                                            cancellable,
480                                            lookup_service_callback, argv[i]);
481         }
482       else if (g_hostname_is_ip_address (argv[i]))
483         {
484           GInetAddress *addr = g_inet_address_new_from_string (argv[i]);
485
486           g_resolver_lookup_by_address_async (resolver, addr, cancellable,
487                                               lookup_by_addr_callback, argv[i]);
488           g_object_unref (addr);
489         }
490       else
491         {
492           g_resolver_lookup_by_name_async (resolver, argv[i], cancellable,
493                                            lookup_by_name_callback,
494                                            argv[i]);
495         }
496
497       /* Stress-test the reloading code */
498       g_signal_emit_by_name (resolver, "reload");
499     }
500 }
501
502 static void
503 print_connectable_sockaddr (GSocketAddress *sockaddr,
504                             GError         *error)
505 {
506   char *phys;
507
508   if (error)
509     {
510       printf ("Error:   %s\n", error->message);
511       g_error_free (error);
512     }
513   else if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
514     {
515       printf ("Error:   Unexpected sockaddr type '%s'\n", g_type_name_from_instance ((GTypeInstance *)sockaddr));
516       g_object_unref (sockaddr);
517     }
518   else
519     {
520       GInetSocketAddress *isa = G_INET_SOCKET_ADDRESS (sockaddr);
521       phys = g_inet_address_to_string (g_inet_socket_address_get_address (isa));
522       printf ("Address: %s%s%s:%d\n",
523               strchr (phys, ':') ? "[" : "", phys, strchr (phys, ':') ? "]" : "",
524               g_inet_socket_address_get_port (isa));
525       g_free (phys);
526       g_object_unref (sockaddr);
527     }
528 }
529
530 static void
531 do_sync_connectable (GSocketAddressEnumerator *enumerator)
532 {
533   GSocketAddress *sockaddr;
534   GError *error = NULL;
535
536   while ((sockaddr = g_socket_address_enumerator_next (enumerator, cancellable, &error)))
537     print_connectable_sockaddr (sockaddr, error);
538
539   g_object_unref (enumerator);
540   done_lookup ();
541 }
542
543 static void do_async_connectable (GSocketAddressEnumerator *enumerator);
544
545 static void
546 got_next_async (GObject *source, GAsyncResult *result, gpointer user_data)
547 {
548   GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (source);
549   GSocketAddress *sockaddr;
550   GError *error = NULL;
551
552   sockaddr = g_socket_address_enumerator_next_finish (enumerator, result, &error);
553   if (sockaddr || error)
554     print_connectable_sockaddr (sockaddr, error);
555   if (sockaddr)
556     do_async_connectable (enumerator);
557   else
558     {
559       g_object_unref (enumerator);
560       done_lookup ();
561     }
562 }
563
564 static void
565 do_async_connectable (GSocketAddressEnumerator *enumerator)
566 {
567   g_socket_address_enumerator_next_async (enumerator, cancellable,
568                                           got_next_async, NULL);
569 }
570
571 static void
572 do_connectable (const char *arg, gboolean synchronous, guint count)
573 {
574   char **parts;
575   GSocketConnectable *connectable;
576   GSocketAddressEnumerator *enumerator;
577
578   if (strchr (arg, '/'))
579     {
580       /* service/protocol/domain */
581       parts = g_strsplit (arg, "/", 3);
582       if (!parts || !parts[2])
583         usage ();
584
585       connectable = g_network_service_new (parts[0], parts[1], parts[2]);
586     }
587   else
588     {
589       guint16 port;
590
591       parts = g_strsplit (arg, ":", 2);
592       if (parts && parts[1])
593         {
594           arg = parts[0];
595           port = strtoul (parts[1], NULL, 10);
596         }
597       else
598         port = 0;
599
600       if (g_hostname_is_ip_address (arg))
601         {
602           GInetAddress *addr = g_inet_address_new_from_string (arg);
603           GSocketAddress *sockaddr = g_inet_socket_address_new (addr, port);
604
605           g_object_unref (addr);
606           connectable = G_SOCKET_CONNECTABLE (sockaddr);
607         }
608       else
609         connectable = g_network_address_new (arg, port);
610     }
611
612   while (count--)
613     {
614       enumerator = g_socket_connectable_enumerate (connectable);
615
616       if (synchronous)
617         do_sync_connectable (enumerator);
618       else
619         do_async_connectable (enumerator);
620     }
621   
622   g_object_unref (connectable);
623 }
624
625 #ifdef G_OS_UNIX
626 static int cancel_fds[2];
627
628 static void
629 interrupted (int sig)
630 {
631   gssize c;
632
633   signal (SIGINT, SIG_DFL);
634   c = write (cancel_fds[1], "x", 1);
635   g_assert_cmpint(c, ==, 1);
636 }
637
638 static gboolean
639 async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancel)
640 {
641   g_cancellable_cancel (cancel);
642   return FALSE;
643 }
644 #endif
645
646
647 static gboolean
648 record_type_arg (const gchar *option_name,
649                  const gchar *value,
650                  gpointer data,
651                  GError **error)
652 {
653   if (g_ascii_strcasecmp (value, "MX") == 0) {
654     record_type = G_RESOLVER_RECORD_MX;
655   } else if (g_ascii_strcasecmp (value, "TXT") == 0) {
656     record_type = G_RESOLVER_RECORD_TXT;
657   } else if (g_ascii_strcasecmp (value, "SOA") == 0) {
658     record_type = G_RESOLVER_RECORD_SOA;
659   } else if (g_ascii_strcasecmp (value, "NS") == 0) {
660     record_type = G_RESOLVER_RECORD_NS;
661   } else {
662       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
663                    "Specify MX, TXT, NS or SOA for the special record lookup types");
664       return FALSE;
665   }
666
667   return TRUE;
668 }
669
670 static const GOptionEntry option_entries[] = {
671   { "synchronous", 's', 0, G_OPTION_ARG_NONE, &synchronous, "Synchronous connections", NULL },
672   { "connectable", 'c', 0, G_OPTION_ARG_INT, &connectable_count, "Connectable count", "C" },
673   { "special-type", 't', 0, G_OPTION_ARG_CALLBACK, record_type_arg, "Record type like MX, TXT, NS or SOA", "RR" },
674   { NULL },
675 };
676
677 int
678 main (int argc, char **argv)
679 {
680   GOptionContext *context;
681   GError *error = NULL;
682 #ifdef G_OS_UNIX
683   GIOChannel *chan;
684   guint watch;
685 #endif
686
687   context = g_option_context_new ("lookups ...");
688   g_option_context_add_main_entries (context, option_entries, NULL);
689   if (!g_option_context_parse (context, &argc, &argv, &error))
690     {
691       g_printerr ("%s\n", error->message);
692       g_error_free (error);
693       usage();
694     }
695
696   if (argc < 2 || (argc > 2 && connectable_count))
697     usage ();
698
699   resolver = g_resolver_get_default ();
700
701   cancellable = g_cancellable_new ();
702
703 #ifdef G_OS_UNIX
704   /* Set up cancellation; we want to cancel if the user ^C's the
705    * program, but we can't cancel directly from an interrupt.
706    */
707   signal (SIGINT, interrupted);
708
709   if (pipe (cancel_fds) == -1)
710     {
711       perror ("pipe");
712       exit (1);
713     }
714   chan = g_io_channel_unix_new (cancel_fds[0]);
715   watch = g_io_add_watch (chan, G_IO_IN, async_cancel, cancellable);
716   g_io_channel_unref (chan);
717 #endif
718
719   nlookups = argc - 1;
720   loop = g_main_loop_new (NULL, TRUE);
721
722   if (connectable_count)
723     {
724       nlookups = connectable_count;
725       do_connectable (argv[1], synchronous, connectable_count);
726     }
727   else
728     {
729       if (synchronous)
730         start_sync_lookups (argv + 1, argc - 1);
731       else
732         start_async_lookups (argv + 1, argc - 1);
733     }
734
735   g_main_loop_run (loop);
736   g_main_loop_unref (loop);
737
738 #ifdef G_OS_UNIX
739   g_source_remove (watch);
740 #endif
741   g_object_unref (cancellable);
742
743   return 0;
744 }