1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3 /* GIO - GLib Input, Output and Streaming Library
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 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, write to the
19 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20 * Boston, MA 02111-1307, USA.
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;
45 static void G_GNUC_NORETURN
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");
58 G_LOCK_DEFINE_STATIC (response);
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...
69 g_idle_add ((GSourceFunc)g_main_loop_quit, loop);
74 print_resolved_name (const char *phys,
79 printf ("Address: %s\n", phys);
82 printf ("Error: %s\n", error->message);
87 printf ("Name: %s\n", name);
97 print_resolved_addresses (const char *name,
105 printf ("Name: %s\n", name);
108 printf ("Error: %s\n", error->message);
109 g_error_free (error);
113 for (a = addresses; a; a = a->next)
115 phys = g_inet_address_to_string (a->data);
116 printf ("Address: %s\n", phys);
118 g_object_unref (a->data);
120 g_list_free (addresses);
129 print_resolved_service (const char *service,
136 printf ("Service: %s\n", service);
139 printf ("Error: %s\n", error->message);
140 g_error_free (error);
144 for (t = targets; t; t = t->next)
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);
153 g_list_free (targets);
162 print_resolved_mx (const char *rrname,
166 const gchar *hostname;
171 printf ("Domain: %s\n", rrname);
174 printf ("Error: %s\n", error->message);
175 g_error_free (error);
179 printf ("no MX records\n");
183 for (t = records; t; t = t->next)
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);
189 g_list_free (records);
198 print_resolved_txt (const char *rrname,
202 const gchar **contents;
207 printf ("Domain: %s\n", rrname);
210 printf ("Error: %s\n", error->message);
211 g_error_free (error);
215 printf ("no TXT records\n");
219 for (t = records; t; t = t->next)
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);
228 g_list_free (records);
237 print_resolved_soa (const char *rrname,
242 const gchar *primary_ns;
243 const gchar *administrator;
244 guint32 serial, refresh, retry, expire, ttl;
247 printf ("Zone: %s\n", rrname);
250 printf ("Error: %s\n", error->message);
251 g_error_free (error);
255 printf ("no SOA records\n");
259 for (t = records; t; t = t->next)
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);
268 g_list_free (records);
277 print_resolved_ns (const char *rrname,
282 const gchar *hostname;
285 printf ("Zone: %s\n", rrname);
288 printf ("Error: %s\n", error->message);
289 g_error_free (error);
293 printf ("no NS records\n");
297 for (t = records; t; t = t->next)
299 g_variant_get (t->data, "(&s)", &hostname);
300 printf ("%s\n", hostname);
301 g_variant_unref (t->data);
303 g_list_free (records);
312 lookup_one_sync (const char *arg)
314 GError *error = NULL;
316 if (record_type != 0)
320 records = g_resolver_lookup_records (resolver, arg, record_type, cancellable, &error);
323 case G_RESOLVER_RECORD_MX:
324 print_resolved_mx (arg, records, error);
326 case G_RESOLVER_RECORD_SOA:
327 print_resolved_soa (arg, records, error);
329 case G_RESOLVER_RECORD_NS:
330 print_resolved_ns (arg, records, error);
332 case G_RESOLVER_RECORD_TXT:
333 print_resolved_txt (arg, records, error);
336 g_warn_if_reached ();
340 else if (strchr (arg, '/'))
343 /* service/protocol/domain */
344 char **parts = g_strsplit (arg, "/", 3);
346 if (!parts || !parts[2])
349 targets = g_resolver_lookup_service (resolver,
350 parts[0], parts[1], parts[2],
351 cancellable, &error);
352 print_resolved_service (arg, targets, error);
354 else if (g_hostname_is_ip_address (arg))
356 GInetAddress *addr = g_inet_address_new_from_string (arg);
359 name = g_resolver_lookup_by_address (resolver, addr, cancellable, &error);
360 print_resolved_name (arg, name, error);
361 g_object_unref (addr);
367 addresses = g_resolver_lookup_by_name (resolver, arg, cancellable, &error);
368 print_resolved_addresses (arg, addresses, error);
373 lookup_thread (gpointer arg)
375 lookup_one_sync (arg);
380 start_sync_lookups (char **argv, int argc)
384 for (i = 0; i < argc; i++)
387 thread = g_thread_new ("lookup", lookup_thread, argv[i]);
388 g_thread_unref (thread);
393 lookup_by_addr_callback (GObject *source, GAsyncResult *result,
396 const char *phys = user_data;
397 GError *error = NULL;
400 name = g_resolver_lookup_by_address_finish (resolver, result, &error);
401 print_resolved_name (phys, name, error);
405 lookup_by_name_callback (GObject *source, GAsyncResult *result,
408 const char *name = user_data;
409 GError *error = NULL;
412 addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
413 print_resolved_addresses (name, addresses, error);
417 lookup_service_callback (GObject *source, GAsyncResult *result,
420 const char *service = user_data;
421 GError *error = NULL;
424 targets = g_resolver_lookup_service_finish (resolver, result, &error);
425 print_resolved_service (service, targets, error);
429 lookup_records_callback (GObject *source,
430 GAsyncResult *result,
433 const char *arg = user_data;
434 GError *error = NULL;
437 records = g_resolver_lookup_records_finish (resolver, result, &error);
441 case G_RESOLVER_RECORD_MX:
442 print_resolved_mx (arg, records, error);
444 case G_RESOLVER_RECORD_SOA:
445 print_resolved_soa (arg, records, error);
447 case G_RESOLVER_RECORD_NS:
448 print_resolved_ns (arg, records, error);
450 case G_RESOLVER_RECORD_TXT:
451 print_resolved_txt (arg, records, error);
454 g_warn_if_reached ();
460 start_async_lookups (char **argv, int argc)
464 for (i = 0; i < argc; i++)
466 if (record_type != 0)
468 g_resolver_lookup_records_async (resolver, argv[i], record_type,
469 cancellable, lookup_records_callback, argv[i]);
471 else if (strchr (argv[i], '/'))
473 /* service/protocol/domain */
474 char **parts = g_strsplit (argv[i], "/", 3);
476 if (!parts || !parts[2])
479 g_resolver_lookup_service_async (resolver,
480 parts[0], parts[1], parts[2],
482 lookup_service_callback, argv[i]);
484 else if (g_hostname_is_ip_address (argv[i]))
486 GInetAddress *addr = g_inet_address_new_from_string (argv[i]);
488 g_resolver_lookup_by_address_async (resolver, addr, cancellable,
489 lookup_by_addr_callback, argv[i]);
490 g_object_unref (addr);
494 g_resolver_lookup_by_name_async (resolver, argv[i], cancellable,
495 lookup_by_name_callback,
499 /* Stress-test the reloading code */
500 g_signal_emit_by_name (resolver, "reload");
505 print_connectable_sockaddr (GSocketAddress *sockaddr,
512 printf ("Error: %s\n", error->message);
513 g_error_free (error);
515 else if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
517 printf ("Error: Unexpected sockaddr type '%s'\n", g_type_name_from_instance ((GTypeInstance *)sockaddr));
518 g_object_unref (sockaddr);
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));
528 g_object_unref (sockaddr);
533 do_sync_connectable (GSocketAddressEnumerator *enumerator)
535 GSocketAddress *sockaddr;
536 GError *error = NULL;
538 while ((sockaddr = g_socket_address_enumerator_next (enumerator, cancellable, &error)))
539 print_connectable_sockaddr (sockaddr, error);
541 g_object_unref (enumerator);
545 static void do_async_connectable (GSocketAddressEnumerator *enumerator);
548 got_next_async (GObject *source, GAsyncResult *result, gpointer user_data)
550 GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (source);
551 GSocketAddress *sockaddr;
552 GError *error = NULL;
554 sockaddr = g_socket_address_enumerator_next_finish (enumerator, result, &error);
555 if (sockaddr || error)
556 print_connectable_sockaddr (sockaddr, error);
558 do_async_connectable (enumerator);
561 g_object_unref (enumerator);
567 do_async_connectable (GSocketAddressEnumerator *enumerator)
569 g_socket_address_enumerator_next_async (enumerator, cancellable,
570 got_next_async, NULL);
574 do_connectable (const char *arg, gboolean synchronous, guint count)
577 GSocketConnectable *connectable;
578 GSocketAddressEnumerator *enumerator;
580 if (strchr (arg, '/'))
582 /* service/protocol/domain */
583 parts = g_strsplit (arg, "/", 3);
584 if (!parts || !parts[2])
587 connectable = g_network_service_new (parts[0], parts[1], parts[2]);
593 parts = g_strsplit (arg, ":", 2);
594 if (parts && parts[1])
597 port = strtoul (parts[1], NULL, 10);
602 if (g_hostname_is_ip_address (arg))
604 GInetAddress *addr = g_inet_address_new_from_string (arg);
605 GSocketAddress *sockaddr = g_inet_socket_address_new (addr, port);
607 g_object_unref (addr);
608 connectable = G_SOCKET_CONNECTABLE (sockaddr);
611 connectable = g_network_address_new (arg, port);
616 enumerator = g_socket_connectable_enumerate (connectable);
619 do_sync_connectable (enumerator);
621 do_async_connectable (enumerator);
624 g_object_unref (connectable);
628 static int cancel_fds[2];
631 interrupted (int sig)
635 signal (SIGINT, SIG_DFL);
636 c = write (cancel_fds[1], "x", 1);
637 g_assert_cmpint(c, ==, 1);
641 async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancel)
643 g_cancellable_cancel (cancel);
650 record_type_arg (const gchar *option_name,
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;
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");
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" },
680 main (int argc, char **argv)
682 GOptionContext *context;
683 GError *error = NULL;
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))
693 g_printerr ("%s\n", error->message);
694 g_error_free (error);
698 if (argc < 2 || (argc > 2 && connectable_count))
701 resolver = g_resolver_get_default ();
703 cancellable = g_cancellable_new ();
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.
709 signal (SIGINT, interrupted);
711 if (pipe (cancel_fds) == -1)
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);
722 loop = g_main_loop_new (NULL, TRUE);
724 if (connectable_count)
726 nlookups = connectable_count;
727 do_connectable (argv[1], synchronous, connectable_count);
732 start_sync_lookups (argv + 1, argc - 1);
734 start_async_lookups (argv + 1, argc - 1);
737 g_main_loop_run (loop);
738 g_main_loop_unref (loop);
741 g_source_remove (watch);
743 g_object_unref (cancellable);