Merge branch '976-disable-assert-checks' into 'master'
[glib.git] / gio / tests / resolver.c
blob85d512428893c72ec6febee077c39a99bb6447f6
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3 /* GIO - GLib Input, Output and Streaming Library
4 *
5 * Copyright (C) 2008 Red Hat, Inc.
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.1 of the License, or (at your option) any later version.
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.
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/>.
21 #include "config.h"
22 #include <glib.h>
23 #include "glibintl.h"
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
33 #include <gio/gio.h>
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;
43 static void G_GNUC_NORETURN
44 usage (void)
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);
56 G_LOCK_DEFINE_STATIC (response);
58 static void
59 done_lookup (void)
61 nlookups--;
62 if (nlookups == 0)
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...
67 g_idle_add ((GSourceFunc)g_main_loop_quit, loop);
71 static void
72 print_resolved_name (const char *phys,
73 char *name,
74 GError *error)
76 G_LOCK (response);
77 printf ("Address: %s\n", phys);
78 if (error)
80 printf ("Error: %s\n", error->message);
81 g_error_free (error);
83 else
85 printf ("Name: %s\n", name);
86 g_free (name);
88 printf ("\n");
90 done_lookup ();
91 G_UNLOCK (response);
94 static void
95 print_resolved_addresses (const char *name,
96 GList *addresses,
97 GError *error)
99 char *phys;
100 GList *a;
102 G_LOCK (response);
103 printf ("Name: %s\n", name);
104 if (error)
106 printf ("Error: %s\n", error->message);
107 g_error_free (error);
109 else
111 for (a = addresses; a; a = a->next)
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);
118 g_list_free (addresses);
120 printf ("\n");
122 done_lookup ();
123 G_UNLOCK (response);
126 static void
127 print_resolved_service (const char *service,
128 GList *targets,
129 GError *error)
131 GList *t;
133 G_LOCK (response);
134 printf ("Service: %s\n", service);
135 if (error)
137 printf ("Error: %s\n", error->message);
138 g_error_free (error);
140 else
142 for (t = targets; t; t = t->next)
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);
151 g_list_free (targets);
153 printf ("\n");
155 done_lookup ();
156 G_UNLOCK (response);
159 static void
160 print_resolved_mx (const char *rrname,
161 GList *records,
162 GError *error)
164 const gchar *hostname;
165 guint16 priority;
166 GList *t;
168 G_LOCK (response);
169 printf ("Domain: %s\n", rrname);
170 if (error)
172 printf ("Error: %s\n", error->message);
173 g_error_free (error);
175 else if (!records)
177 printf ("no MX records\n");
179 else
181 for (t = records; t; t = t->next)
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);
187 g_list_free (records);
189 printf ("\n");
191 done_lookup ();
192 G_UNLOCK (response);
195 static void
196 print_resolved_txt (const char *rrname,
197 GList *records,
198 GError *error)
200 const gchar **contents;
201 GList *t;
202 gint i;
204 G_LOCK (response);
205 printf ("Domain: %s\n", rrname);
206 if (error)
208 printf ("Error: %s\n", error->message);
209 g_error_free (error);
211 else if (!records)
213 printf ("no TXT records\n");
215 else
217 for (t = records; t; t = t->next)
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);
226 g_list_free (records);
228 printf ("\n");
230 done_lookup ();
231 G_UNLOCK (response);
234 static void
235 print_resolved_soa (const char *rrname,
236 GList *records,
237 GError *error)
239 GList *t;
240 const gchar *primary_ns;
241 const gchar *administrator;
242 guint32 serial, refresh, retry, expire, ttl;
244 G_LOCK (response);
245 printf ("Zone: %s\n", rrname);
246 if (error)
248 printf ("Error: %s\n", error->message);
249 g_error_free (error);
251 else if (!records)
253 printf ("no SOA records\n");
255 else
257 for (t = records; t; t = t->next)
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);
266 g_list_free (records);
268 printf ("\n");
270 done_lookup ();
271 G_UNLOCK (response);
274 static void
275 print_resolved_ns (const char *rrname,
276 GList *records,
277 GError *error)
279 GList *t;
280 const gchar *hostname;
282 G_LOCK (response);
283 printf ("Zone: %s\n", rrname);
284 if (error)
286 printf ("Error: %s\n", error->message);
287 g_error_free (error);
289 else if (!records)
291 printf ("no NS records\n");
293 else
295 for (t = records; t; t = t->next)
297 g_variant_get (t->data, "(&s)", &hostname);
298 printf ("%s\n", hostname);
299 g_variant_unref (t->data);
301 g_list_free (records);
303 printf ("\n");
305 done_lookup ();
306 G_UNLOCK (response);
309 static void
310 lookup_one_sync (const char *arg)
312 GError *error = NULL;
314 if (record_type != 0)
316 GList *records;
318 records = g_resolver_lookup_records (resolver, arg, record_type, cancellable, &error);
319 switch (record_type)
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;
338 else if (strchr (arg, '/'))
340 GList *targets;
341 /* service/protocol/domain */
342 char **parts = g_strsplit (arg, "/", 3);
344 if (!parts || !parts[2])
345 usage ();
347 targets = g_resolver_lookup_service (resolver,
348 parts[0], parts[1], parts[2],
349 cancellable, &error);
350 print_resolved_service (arg, targets, error);
352 else if (g_hostname_is_ip_address (arg))
354 GInetAddress *addr = g_inet_address_new_from_string (arg);
355 char *name;
357 name = g_resolver_lookup_by_address (resolver, addr, cancellable, &error);
358 print_resolved_name (arg, name, error);
359 g_object_unref (addr);
361 else
363 GList *addresses;
365 addresses = g_resolver_lookup_by_name (resolver, arg, cancellable, &error);
366 print_resolved_addresses (arg, addresses, error);
370 static gpointer
371 lookup_thread (gpointer arg)
373 lookup_one_sync (arg);
374 return NULL;
377 static void
378 start_sync_lookups (char **argv, int argc)
380 int i;
382 for (i = 0; i < argc; i++)
384 GThread *thread;
385 thread = g_thread_new ("lookup", lookup_thread, argv[i]);
386 g_thread_unref (thread);
390 static void
391 lookup_by_addr_callback (GObject *source, GAsyncResult *result,
392 gpointer user_data)
394 const char *phys = user_data;
395 GError *error = NULL;
396 char *name;
398 name = g_resolver_lookup_by_address_finish (resolver, result, &error);
399 print_resolved_name (phys, name, error);
402 static void
403 lookup_by_name_callback (GObject *source, GAsyncResult *result,
404 gpointer user_data)
406 const char *name = user_data;
407 GError *error = NULL;
408 GList *addresses;
410 addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
411 print_resolved_addresses (name, addresses, error);
414 static void
415 lookup_service_callback (GObject *source, GAsyncResult *result,
416 gpointer user_data)
418 const char *service = user_data;
419 GError *error = NULL;
420 GList *targets;
422 targets = g_resolver_lookup_service_finish (resolver, result, &error);
423 print_resolved_service (service, targets, error);
426 static void
427 lookup_records_callback (GObject *source,
428 GAsyncResult *result,
429 gpointer user_data)
431 const char *arg = user_data;
432 GError *error = NULL;
433 GList *records;
435 records = g_resolver_lookup_records_finish (resolver, result, &error);
437 switch (record_type)
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;
457 static void
458 start_async_lookups (char **argv, int argc)
460 int i;
462 for (i = 0; i < argc; i++)
464 if (record_type != 0)
466 g_resolver_lookup_records_async (resolver, argv[i], record_type,
467 cancellable, lookup_records_callback, argv[i]);
469 else if (strchr (argv[i], '/'))
471 /* service/protocol/domain */
472 char **parts = g_strsplit (argv[i], "/", 3);
474 if (!parts || !parts[2])
475 usage ();
477 g_resolver_lookup_service_async (resolver,
478 parts[0], parts[1], parts[2],
479 cancellable,
480 lookup_service_callback, argv[i]);
482 else if (g_hostname_is_ip_address (argv[i]))
484 GInetAddress *addr = g_inet_address_new_from_string (argv[i]);
486 g_resolver_lookup_by_address_async (resolver, addr, cancellable,
487 lookup_by_addr_callback, argv[i]);
488 g_object_unref (addr);
490 else
492 g_resolver_lookup_by_name_async (resolver, argv[i], cancellable,
493 lookup_by_name_callback,
494 argv[i]);
497 /* Stress-test the reloading code */
498 g_signal_emit_by_name (resolver, "reload");
502 static void
503 print_connectable_sockaddr (GSocketAddress *sockaddr,
504 GError *error)
506 char *phys;
508 if (error)
510 printf ("Error: %s\n", error->message);
511 g_error_free (error);
513 else if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
515 printf ("Error: Unexpected sockaddr type '%s'\n", g_type_name_from_instance ((GTypeInstance *)sockaddr));
516 g_object_unref (sockaddr);
518 else
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);
530 static void
531 do_sync_connectable (GSocketAddressEnumerator *enumerator)
533 GSocketAddress *sockaddr;
534 GError *error = NULL;
536 while ((sockaddr = g_socket_address_enumerator_next (enumerator, cancellable, &error)))
537 print_connectable_sockaddr (sockaddr, error);
539 g_object_unref (enumerator);
540 done_lookup ();
543 static void do_async_connectable (GSocketAddressEnumerator *enumerator);
545 static void
546 got_next_async (GObject *source, GAsyncResult *result, gpointer user_data)
548 GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (source);
549 GSocketAddress *sockaddr;
550 GError *error = NULL;
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
559 g_object_unref (enumerator);
560 done_lookup ();
564 static void
565 do_async_connectable (GSocketAddressEnumerator *enumerator)
567 g_socket_address_enumerator_next_async (enumerator, cancellable,
568 got_next_async, NULL);
571 static void
572 do_connectable (const char *arg, gboolean synchronous, guint count)
574 char **parts;
575 GSocketConnectable *connectable;
576 GSocketAddressEnumerator *enumerator;
578 if (strchr (arg, '/'))
580 /* service/protocol/domain */
581 parts = g_strsplit (arg, "/", 3);
582 if (!parts || !parts[2])
583 usage ();
585 connectable = g_network_service_new (parts[0], parts[1], parts[2]);
587 else
589 guint16 port;
591 parts = g_strsplit (arg, ":", 2);
592 if (parts && parts[1])
594 arg = parts[0];
595 port = strtoul (parts[1], NULL, 10);
597 else
598 port = 0;
600 if (g_hostname_is_ip_address (arg))
602 GInetAddress *addr = g_inet_address_new_from_string (arg);
603 GSocketAddress *sockaddr = g_inet_socket_address_new (addr, port);
605 g_object_unref (addr);
606 connectable = G_SOCKET_CONNECTABLE (sockaddr);
608 else
609 connectable = g_network_address_new (arg, port);
612 while (count--)
614 enumerator = g_socket_connectable_enumerate (connectable);
616 if (synchronous)
617 do_sync_connectable (enumerator);
618 else
619 do_async_connectable (enumerator);
622 g_object_unref (connectable);
625 #ifdef G_OS_UNIX
626 static int cancel_fds[2];
628 static void
629 interrupted (int sig)
631 gssize c;
633 signal (SIGINT, SIG_DFL);
634 c = write (cancel_fds[1], "x", 1);
635 g_assert_cmpint(c, ==, 1);
638 static gboolean
639 async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancel)
641 g_cancellable_cancel (cancel);
642 return FALSE;
644 #endif
647 static gboolean
648 record_type_arg (const gchar *option_name,
649 const gchar *value,
650 gpointer data,
651 GError **error)
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;
667 return TRUE;
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 },
678 main (int argc, char **argv)
680 GOptionContext *context;
681 GError *error = NULL;
682 #ifdef G_OS_UNIX
683 GIOChannel *chan;
684 guint watch;
685 #endif
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))
691 g_printerr ("%s\n", error->message);
692 g_error_free (error);
693 usage();
696 if (argc < 2 || (argc > 2 && connectable_count))
697 usage ();
699 resolver = g_resolver_get_default ();
701 cancellable = g_cancellable_new ();
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.
707 signal (SIGINT, interrupted);
709 if (pipe (cancel_fds) == -1)
711 perror ("pipe");
712 exit (1);
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
719 nlookups = argc - 1;
720 loop = g_main_loop_new (NULL, TRUE);
722 if (connectable_count)
724 nlookups = connectable_count;
725 do_connectable (argv[1], synchronous, connectable_count);
727 else
729 if (synchronous)
730 start_sync_lookups (argv + 1, argc - 1);
731 else
732 start_async_lookups (argv + 1, argc - 1);
735 g_main_loop_run (loop);
736 g_main_loop_unref (loop);
738 #ifdef G_OS_UNIX
739 g_source_remove (watch);
740 #endif
741 g_object_unref (cancellable);
743 return 0;