Add GResolver, a glib-ish interface to DNS
[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 #include <unistd.h>
32
33 #include <gio/gio.h>
34
35 static GResolver *resolver;
36 static GCancellable *cancellable;
37 static GMainLoop *loop;
38 static int nlookups = 0;
39
40 static void
41 usage (void)
42 {
43         fprintf (stderr, "Usage: resolver [-t] [-s] [hostname | IP | service/protocol/domain ] ...\n");
44         fprintf (stderr, "       Use -t to enable threading.\n");
45         fprintf (stderr, "       Use -s to do synchronous lookups.\n");
46         fprintf (stderr, "       Both together will result in simultaneous lookups in multiple threads\n");
47         exit (1);
48 }
49
50 G_LOCK_DEFINE_STATIC (response);
51
52 static void
53 done_lookup (void)
54 {
55   nlookups--;
56   if (nlookups == 0)
57     {
58       /* In the sync case we need to make sure we don't call
59        * g_main_loop_quit before the loop is actually running...
60        */
61       g_idle_add ((GSourceFunc)g_main_loop_quit, loop);
62     }
63 }
64
65 static void
66 print_resolved_name (const char *phys,
67                      char       *name,
68                      GError     *error)
69 {
70   G_LOCK (response);
71   printf ("Address: %s\n", phys);
72   if (error)
73     {
74       printf ("Error:   %s\n", error->message);
75       g_error_free (error);
76     }
77   else
78     {
79       printf ("Name:    %s\n", name);
80       g_free (name);
81     }
82   printf ("\n");
83
84   done_lookup ();
85   G_UNLOCK (response);
86 }
87
88 static void
89 print_resolved_addresses (const char *name,
90                           GList      *addresses,
91                           GError     *error)
92 {
93   char *phys;
94   GList *a;
95
96   G_LOCK (response);
97   printf ("Name:    %s\n", name);
98   if (error)
99     {
100       printf ("Error:   %s\n", error->message);
101       g_error_free (error);
102     }
103   else
104     {
105       for (a = addresses; a; a = a->next)
106         {
107           phys = g_inet_address_to_string (a->data);
108           printf ("Address: %s\n", phys);
109           g_free (phys);
110           g_object_unref (a->data);
111         }
112       g_list_free (addresses);
113     }
114   printf ("\n");
115
116   done_lookup ();
117   G_UNLOCK (response);
118 }
119
120 static void
121 print_resolved_service (const char *service,
122                         GList      *targets,
123                         GError     *error)
124 {
125   GList *t;  
126
127   G_LOCK (response);
128   printf ("Service: %s\n", service);
129   if (error)
130     {
131       printf ("Error: %s\n", error->message);
132       g_error_free (error);
133     }
134   else
135     {
136       for (t = targets; t; t = t->next)
137         {
138           printf ("%s:%u (pri %u, weight %u)\n",
139                   g_srv_target_get_hostname (t->data),
140                   g_srv_target_get_port (t->data),
141                   g_srv_target_get_priority (t->data),
142                   g_srv_target_get_weight (t->data));
143           g_srv_target_free (t->data);
144         }
145       g_list_free (targets);
146     }
147   printf ("\n");
148
149   done_lookup ();
150   G_UNLOCK (response);
151 }
152
153 static void
154 lookup_one_sync (const char *arg)
155 {
156   GError *error = NULL;
157
158   if (strchr (arg, '/'))
159     {
160       GList *targets;
161       /* service/protocol/domain */
162       char **parts = g_strsplit (arg, "/", 3);
163
164       if (!parts || !parts[2])
165         usage ();
166
167       targets = g_resolver_lookup_service (resolver,
168                                            parts[0], parts[1], parts[2],
169                                            cancellable, &error);
170       print_resolved_service (arg, targets, error);
171     }
172   else if (g_hostname_is_ip_address (arg))
173     {
174       GInetAddress *addr = g_inet_address_new_from_string (arg);
175       char *name;
176
177       name = g_resolver_lookup_by_address (resolver, addr, cancellable, &error);
178       print_resolved_name (arg, name, error);
179       g_object_unref (addr);
180     }
181   else
182     {
183       GList *addresses;
184
185       addresses = g_resolver_lookup_by_name (resolver, arg, cancellable, &error);
186       print_resolved_addresses (arg, addresses, error);
187     }
188 }
189
190 static gpointer
191 lookup_thread (gpointer arg)
192 {
193   lookup_one_sync (arg);
194   return NULL;
195 }
196
197 static void
198 start_threaded_lookups (char **argv, int argc)
199 {
200   int i;
201
202   for (i = 0; i < argc; i++)
203     g_thread_create (lookup_thread, argv[i], FALSE, NULL);
204 }
205
206 static void
207 start_sync_lookups (char **argv, int argc)
208 {
209   int i;
210
211   for (i = 0; i < argc; i++)
212     lookup_one_sync (argv[i]);
213 }
214
215 static void
216 lookup_by_addr_callback (GObject *source, GAsyncResult *result,
217                          gpointer user_data)
218 {
219   const char *phys = user_data;
220   GError *error = NULL;
221   char *name;
222
223   name = g_resolver_lookup_by_address_finish (resolver, result, &error);
224   print_resolved_name (phys, name, error);
225 }
226
227 static void
228 lookup_by_name_callback (GObject *source, GAsyncResult *result,
229                          gpointer user_data)
230 {
231   const char *name = user_data;
232   GError *error = NULL;
233   GList *addresses;
234
235   addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
236   print_resolved_addresses (name, addresses, error);
237 }
238
239 static void
240 lookup_service_callback (GObject *source, GAsyncResult *result,
241                          gpointer user_data)
242 {
243   const char *service = user_data;
244   GError *error = NULL;
245   GList *targets;
246
247   targets = g_resolver_lookup_service_finish (resolver, result, &error);
248   print_resolved_service (service, targets, error);
249 }
250
251 static void
252 start_async_lookups (char **argv, int argc)
253 {
254   int i;
255
256   for (i = 0; i < argc; i++)
257     {
258       if (strchr (argv[i], '/'))
259         {
260           /* service/protocol/domain */
261           char **parts = g_strsplit (argv[i], "/", 3);
262
263           if (!parts || !parts[2])
264             usage ();
265
266           g_resolver_lookup_service_async (resolver,
267                                            parts[0], parts[1], parts[2],
268                                            cancellable,
269                                            lookup_service_callback, argv[i]);
270         }
271       else if (g_hostname_is_ip_address (argv[i]))
272         {
273           GInetAddress *addr = g_inet_address_new_from_string (argv[i]);
274
275           g_resolver_lookup_by_address_async (resolver, addr, cancellable,
276                                               lookup_by_addr_callback, argv[i]);
277           g_object_unref (addr);
278         }
279       else
280         {
281           g_resolver_lookup_by_name_async (resolver, argv[i], cancellable,
282                                            lookup_by_name_callback,
283                                            argv[i]);
284         }
285     }
286 }
287
288 #ifdef G_OS_UNIX
289 static int cancel_fds[2];
290
291 static void
292 interrupted (int sig)
293 {
294   signal (SIGINT, SIG_DFL);
295   write (cancel_fds[1], "x", 1);
296 }
297
298 static gboolean
299 async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancellable)
300 {
301   g_cancellable_cancel (cancellable);
302   return FALSE;
303 }
304 #endif
305
306 int
307 main (int argc, char **argv)
308 {
309   gboolean threaded = FALSE, synchronous = FALSE;
310 #ifdef G_OS_UNIX
311   GIOChannel *chan;
312   guint watch;
313 #endif
314
315   /* We can't use GOptionContext because we use the arguments to
316    * decide whether or not to call g_thread_init().
317    */
318   while (argc >= 2 && argv[1][0] == '-')
319     {
320       if (!strcmp (argv[1], "-t"))
321         {
322           g_thread_init (NULL);
323           threaded = TRUE;
324         }
325       else if (!strcmp (argv[1], "-s"))
326         synchronous = TRUE;
327       else
328         usage ();
329
330       argv++;
331       argc--;
332     }
333   g_type_init ();
334
335   if (argc < 2)
336     usage ();
337
338   resolver = g_resolver_get_default ();
339
340   cancellable = g_cancellable_new ();
341
342 #ifdef G_OS_UNIX
343   /* Set up cancellation; we want to cancel if the user ^C's the
344    * program, but we can't cancel directly from an interrupt.
345    */
346   signal (SIGINT, interrupted);
347
348   if (pipe (cancel_fds) == -1)
349     {
350       perror ("pipe");
351       exit (1);
352     }
353   chan = g_io_channel_unix_new (cancel_fds[0]);
354   watch = g_io_add_watch (chan, G_IO_IN, async_cancel, cancellable);
355   g_io_channel_unref (chan);
356 #endif
357
358   nlookups = argc - 1;
359   loop = g_main_loop_new (NULL, TRUE);
360
361   if (threaded && synchronous)
362     start_threaded_lookups (argv + 1, argc - 1);
363   else if (synchronous)
364     start_sync_lookups (argv + 1, argc - 1);
365   else
366     start_async_lookups (argv + 1, argc - 1);
367
368   g_main_run (loop);
369   g_main_loop_unref (loop);
370
371 #ifdef G_OS_UNIX
372   g_source_remove (watch);
373 #endif
374   g_object_unref (cancellable);
375
376   return 0;
377 }