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, see <http://www.gnu.org/licenses/>.
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
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");
56 G_LOCK_DEFINE_STATIC (response
);
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
);
72 print_resolved_name (const char *phys
,
77 printf ("Address: %s\n", phys
);
80 printf ("Error: %s\n", error
->message
);
85 printf ("Name: %s\n", name
);
95 print_resolved_addresses (const char *name
,
103 printf ("Name: %s\n", name
);
106 printf ("Error: %s\n", error
->message
);
107 g_error_free (error
);
111 for (a
= addresses
; a
; a
= a
->next
)
113 phys
= g_inet_address_to_string (a
->data
);
114 printf ("Address: %s\n", phys
);
116 g_object_unref (a
->data
);
118 g_list_free (addresses
);
127 print_resolved_service (const char *service
,
134 printf ("Service: %s\n", service
);
137 printf ("Error: %s\n", error
->message
);
138 g_error_free (error
);
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
);
160 print_resolved_mx (const char *rrname
,
164 const gchar
*hostname
;
169 printf ("Domain: %s\n", rrname
);
172 printf ("Error: %s\n", error
->message
);
173 g_error_free (error
);
177 printf ("no MX records\n");
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
);
196 print_resolved_txt (const char *rrname
,
200 const gchar
**contents
;
205 printf ("Domain: %s\n", rrname
);
208 printf ("Error: %s\n", error
->message
);
209 g_error_free (error
);
213 printf ("no TXT records\n");
217 for (t
= records
; t
; t
= t
->next
)
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
);
235 print_resolved_soa (const char *rrname
,
240 const gchar
*primary_ns
;
241 const gchar
*administrator
;
242 guint32 serial
, refresh
, retry
, expire
, ttl
;
245 printf ("Zone: %s\n", rrname
);
248 printf ("Error: %s\n", error
->message
);
249 g_error_free (error
);
253 printf ("no SOA records\n");
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
);
275 print_resolved_ns (const char *rrname
,
280 const gchar
*hostname
;
283 printf ("Zone: %s\n", rrname
);
286 printf ("Error: %s\n", error
->message
);
287 g_error_free (error
);
291 printf ("no NS records\n");
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
);
310 lookup_one_sync (const char *arg
)
312 GError
*error
= NULL
;
314 if (record_type
!= 0)
318 records
= g_resolver_lookup_records (resolver
, arg
, record_type
, cancellable
, &error
);
321 case G_RESOLVER_RECORD_MX
:
322 print_resolved_mx (arg
, records
, error
);
324 case G_RESOLVER_RECORD_SOA
:
325 print_resolved_soa (arg
, records
, error
);
327 case G_RESOLVER_RECORD_NS
:
328 print_resolved_ns (arg
, records
, error
);
330 case G_RESOLVER_RECORD_TXT
:
331 print_resolved_txt (arg
, records
, error
);
334 g_warn_if_reached ();
338 else if (strchr (arg
, '/'))
341 /* service/protocol/domain */
342 char **parts
= g_strsplit (arg
, "/", 3);
344 if (!parts
|| !parts
[2])
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
);
357 name
= g_resolver_lookup_by_address (resolver
, addr
, cancellable
, &error
);
358 print_resolved_name (arg
, name
, error
);
359 g_object_unref (addr
);
365 addresses
= g_resolver_lookup_by_name (resolver
, arg
, cancellable
, &error
);
366 print_resolved_addresses (arg
, addresses
, error
);
371 lookup_thread (gpointer arg
)
373 lookup_one_sync (arg
);
378 start_sync_lookups (char **argv
, int argc
)
382 for (i
= 0; i
< argc
; i
++)
385 thread
= g_thread_new ("lookup", lookup_thread
, argv
[i
]);
386 g_thread_unref (thread
);
391 lookup_by_addr_callback (GObject
*source
, GAsyncResult
*result
,
394 const char *phys
= user_data
;
395 GError
*error
= NULL
;
398 name
= g_resolver_lookup_by_address_finish (resolver
, result
, &error
);
399 print_resolved_name (phys
, name
, error
);
403 lookup_by_name_callback (GObject
*source
, GAsyncResult
*result
,
406 const char *name
= user_data
;
407 GError
*error
= NULL
;
410 addresses
= g_resolver_lookup_by_name_finish (resolver
, result
, &error
);
411 print_resolved_addresses (name
, addresses
, error
);
415 lookup_service_callback (GObject
*source
, GAsyncResult
*result
,
418 const char *service
= user_data
;
419 GError
*error
= NULL
;
422 targets
= g_resolver_lookup_service_finish (resolver
, result
, &error
);
423 print_resolved_service (service
, targets
, error
);
427 lookup_records_callback (GObject
*source
,
428 GAsyncResult
*result
,
431 const char *arg
= user_data
;
432 GError
*error
= NULL
;
435 records
= g_resolver_lookup_records_finish (resolver
, result
, &error
);
439 case G_RESOLVER_RECORD_MX
:
440 print_resolved_mx (arg
, records
, error
);
442 case G_RESOLVER_RECORD_SOA
:
443 print_resolved_soa (arg
, records
, error
);
445 case G_RESOLVER_RECORD_NS
:
446 print_resolved_ns (arg
, records
, error
);
448 case G_RESOLVER_RECORD_TXT
:
449 print_resolved_txt (arg
, records
, error
);
452 g_warn_if_reached ();
458 start_async_lookups (char **argv
, int argc
)
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])
477 g_resolver_lookup_service_async (resolver
,
478 parts
[0], parts
[1], parts
[2],
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
);
492 g_resolver_lookup_by_name_async (resolver
, argv
[i
], cancellable
,
493 lookup_by_name_callback
,
497 /* Stress-test the reloading code */
498 g_signal_emit_by_name (resolver
, "reload");
503 print_connectable_sockaddr (GSocketAddress
*sockaddr
,
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
);
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
));
526 g_object_unref (sockaddr
);
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
);
543 static void do_async_connectable (GSocketAddressEnumerator
*enumerator
);
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
);
556 do_async_connectable (enumerator
);
559 g_object_unref (enumerator
);
565 do_async_connectable (GSocketAddressEnumerator
*enumerator
)
567 g_socket_address_enumerator_next_async (enumerator
, cancellable
,
568 got_next_async
, NULL
);
572 do_connectable (const char *arg
, gboolean synchronous
, guint count
)
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])
585 connectable
= g_network_service_new (parts
[0], parts
[1], parts
[2]);
591 parts
= g_strsplit (arg
, ":", 2);
592 if (parts
&& parts
[1])
595 port
= strtoul (parts
[1], NULL
, 10);
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
);
609 connectable
= g_network_address_new (arg
, port
);
614 enumerator
= g_socket_connectable_enumerate (connectable
);
617 do_sync_connectable (enumerator
);
619 do_async_connectable (enumerator
);
622 g_object_unref (connectable
);
626 static int cancel_fds
[2];
629 interrupted (int sig
)
633 signal (SIGINT
, SIG_DFL
);
634 c
= write (cancel_fds
[1], "x", 1);
635 g_assert_cmpint(c
, ==, 1);
639 async_cancel (GIOChannel
*source
, GIOCondition cond
, gpointer cancel
)
641 g_cancellable_cancel (cancel
);
648 record_type_arg (const gchar
*option_name
,
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
;
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");
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" },
678 main (int argc
, char **argv
)
680 GOptionContext
*context
;
681 GError
*error
= NULL
;
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
);
696 if (argc
< 2 || (argc
> 2 && connectable_count
))
699 resolver
= g_resolver_get_default ();
701 cancellable
= g_cancellable_new ();
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)
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
);
720 loop
= g_main_loop_new (NULL
, TRUE
);
722 if (connectable_count
)
724 nlookups
= connectable_count
;
725 do_connectable (argv
[1], synchronous
, connectable_count
);
730 start_sync_lookups (argv
+ 1, argc
- 1);
732 start_async_lookups (argv
+ 1, argc
- 1);
735 g_main_loop_run (loop
);
736 g_main_loop_unref (loop
);
739 g_source_remove (watch
);
741 g_object_unref (cancellable
);