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