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