Merge remote-tracking branch 'gvdb/master'
[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 G_GNUC_NORETURN
41 usage (void)
42 {
43         fprintf (stderr, "Usage: resolver [-s] [hostname | IP | service/protocol/domain ] ...\n");
44         fprintf (stderr, "       resolver [-s] -c NUMBER [hostname | IP | service/protocol/domain ]\n");
45         fprintf (stderr, "       Use -s to do synchronous lookups.\n");
46         fprintf (stderr, "       Use -c NUMBER (and only a single resolvable argument) to test GSocketConnectable.\n");
47         fprintf (stderr, "       The given NUMBER determines how many times the connectable will be enumerated.\n");
48         exit (1);
49 }
50
51 G_LOCK_DEFINE_STATIC (response);
52
53 static void
54 done_lookup (void)
55 {
56   nlookups--;
57   if (nlookups == 0)
58     {
59       /* In the sync case we need to make sure we don't call
60        * g_main_loop_quit before the loop is actually running...
61        */
62       g_idle_add ((GSourceFunc)g_main_loop_quit, loop);
63     }
64 }
65
66 static void
67 print_resolved_name (const char *phys,
68                      char       *name,
69                      GError     *error)
70 {
71   G_LOCK (response);
72   printf ("Address: %s\n", phys);
73   if (error)
74     {
75       printf ("Error:   %s\n", error->message);
76       g_error_free (error);
77     }
78   else
79     {
80       printf ("Name:    %s\n", name);
81       g_free (name);
82     }
83   printf ("\n");
84
85   done_lookup ();
86   G_UNLOCK (response);
87 }
88
89 static void
90 print_resolved_addresses (const char *name,
91                           GList      *addresses,
92                           GError     *error)
93 {
94   char *phys;
95   GList *a;
96
97   G_LOCK (response);
98   printf ("Name:    %s\n", name);
99   if (error)
100     {
101       printf ("Error:   %s\n", error->message);
102       g_error_free (error);
103     }
104   else
105     {
106       for (a = addresses; a; a = a->next)
107         {
108           phys = g_inet_address_to_string (a->data);
109           printf ("Address: %s\n", phys);
110           g_free (phys);
111           g_object_unref (a->data);
112         }
113       g_list_free (addresses);
114     }
115   printf ("\n");
116
117   done_lookup ();
118   G_UNLOCK (response);
119 }
120
121 static void
122 print_resolved_service (const char *service,
123                         GList      *targets,
124                         GError     *error)
125 {
126   GList *t;  
127
128   G_LOCK (response);
129   printf ("Service: %s\n", service);
130   if (error)
131     {
132       printf ("Error: %s\n", error->message);
133       g_error_free (error);
134     }
135   else
136     {
137       for (t = targets; t; t = t->next)
138         {
139           printf ("%s:%u (pri %u, weight %u)\n",
140                   g_srv_target_get_hostname (t->data),
141                   g_srv_target_get_port (t->data),
142                   g_srv_target_get_priority (t->data),
143                   g_srv_target_get_weight (t->data));
144           g_srv_target_free (t->data);
145         }
146       g_list_free (targets);
147     }
148   printf ("\n");
149
150   done_lookup ();
151   G_UNLOCK (response);
152 }
153
154 static void
155 lookup_one_sync (const char *arg)
156 {
157   GError *error = NULL;
158
159   if (strchr (arg, '/'))
160     {
161       GList *targets;
162       /* service/protocol/domain */
163       char **parts = g_strsplit (arg, "/", 3);
164
165       if (!parts || !parts[2])
166         usage ();
167
168       targets = g_resolver_lookup_service (resolver,
169                                            parts[0], parts[1], parts[2],
170                                            cancellable, &error);
171       print_resolved_service (arg, targets, error);
172     }
173   else if (g_hostname_is_ip_address (arg))
174     {
175       GInetAddress *addr = g_inet_address_new_from_string (arg);
176       char *name;
177
178       name = g_resolver_lookup_by_address (resolver, addr, cancellable, &error);
179       print_resolved_name (arg, name, error);
180       g_object_unref (addr);
181     }
182   else
183     {
184       GList *addresses;
185
186       addresses = g_resolver_lookup_by_name (resolver, arg, cancellable, &error);
187       print_resolved_addresses (arg, addresses, error);
188     }
189 }
190
191 static gpointer
192 lookup_thread (gpointer arg)
193 {
194   lookup_one_sync (arg);
195   return NULL;
196 }
197
198 static void
199 start_sync_lookups (char **argv, int argc)
200 {
201   int i;
202
203   for (i = 0; i < argc; i++)
204     g_thread_create (lookup_thread, argv[i], FALSE, NULL);
205 }
206
207 static void
208 lookup_by_addr_callback (GObject *source, GAsyncResult *result,
209                          gpointer user_data)
210 {
211   const char *phys = user_data;
212   GError *error = NULL;
213   char *name;
214
215   name = g_resolver_lookup_by_address_finish (resolver, result, &error);
216   print_resolved_name (phys, name, error);
217 }
218
219 static void
220 lookup_by_name_callback (GObject *source, GAsyncResult *result,
221                          gpointer user_data)
222 {
223   const char *name = user_data;
224   GError *error = NULL;
225   GList *addresses;
226
227   addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
228   print_resolved_addresses (name, addresses, error);
229 }
230
231 static void
232 lookup_service_callback (GObject *source, GAsyncResult *result,
233                          gpointer user_data)
234 {
235   const char *service = user_data;
236   GError *error = NULL;
237   GList *targets;
238
239   targets = g_resolver_lookup_service_finish (resolver, result, &error);
240   print_resolved_service (service, targets, error);
241 }
242
243 static void
244 start_async_lookups (char **argv, int argc)
245 {
246   int i;
247
248   for (i = 0; i < argc; i++)
249     {
250       if (strchr (argv[i], '/'))
251         {
252           /* service/protocol/domain */
253           char **parts = g_strsplit (argv[i], "/", 3);
254
255           if (!parts || !parts[2])
256             usage ();
257
258           g_resolver_lookup_service_async (resolver,
259                                            parts[0], parts[1], parts[2],
260                                            cancellable,
261                                            lookup_service_callback, argv[i]);
262         }
263       else if (g_hostname_is_ip_address (argv[i]))
264         {
265           GInetAddress *addr = g_inet_address_new_from_string (argv[i]);
266
267           g_resolver_lookup_by_address_async (resolver, addr, cancellable,
268                                               lookup_by_addr_callback, argv[i]);
269           g_object_unref (addr);
270         }
271       else
272         {
273           g_resolver_lookup_by_name_async (resolver, argv[i], cancellable,
274                                            lookup_by_name_callback,
275                                            argv[i]);
276         }
277
278       /* Stress-test the reloading code */
279       g_signal_emit_by_name (resolver, "reload");
280     }
281 }
282
283 static void
284 print_connectable_sockaddr (GSocketAddress *sockaddr,
285                             GError         *error)
286 {
287   char *phys;
288
289   if (error)
290     {
291       printf ("Error:   %s\n", error->message);
292       g_error_free (error);
293     }
294   else if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
295     {
296       printf ("Error:   Unexpected sockaddr type '%s'\n", g_type_name_from_instance ((GTypeInstance *)sockaddr));
297       g_object_unref (sockaddr);
298     }
299   else
300     {
301       GInetSocketAddress *isa = G_INET_SOCKET_ADDRESS (sockaddr);
302       phys = g_inet_address_to_string (g_inet_socket_address_get_address (isa));
303       printf ("Address: %s%s%s:%d\n",
304               strchr (phys, ':') ? "[" : "", phys, strchr (phys, ':') ? "]" : "",
305               g_inet_socket_address_get_port (isa));
306       g_free (phys);
307       g_object_unref (sockaddr);
308     }
309 }
310
311 static void
312 do_sync_connectable (GSocketAddressEnumerator *enumerator)
313 {
314   GSocketAddress *sockaddr;
315   GError *error = NULL;
316
317   while ((sockaddr = g_socket_address_enumerator_next (enumerator, cancellable, &error)))
318     print_connectable_sockaddr (sockaddr, error);
319
320   g_object_unref (enumerator);
321   done_lookup ();
322 }
323
324 static void do_async_connectable (GSocketAddressEnumerator *enumerator);
325
326 static void
327 got_next_async (GObject *source, GAsyncResult *result, gpointer user_data)
328 {
329   GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (source);
330   GSocketAddress *sockaddr;
331   GError *error = NULL;
332
333   sockaddr = g_socket_address_enumerator_next_finish (enumerator, result, &error);
334   if (sockaddr || error)
335     print_connectable_sockaddr (sockaddr, error);
336   if (sockaddr)
337     do_async_connectable (enumerator);
338   else
339     {
340       g_object_unref (enumerator);
341       done_lookup ();
342     }
343 }
344
345 static void
346 do_async_connectable (GSocketAddressEnumerator *enumerator)
347 {
348   g_socket_address_enumerator_next_async (enumerator, cancellable,
349                                           got_next_async, NULL);
350 }
351
352 static void
353 do_connectable (const char *arg, gboolean synchronous, guint count)
354 {
355   char **parts;
356   GSocketConnectable *connectable;
357   GSocketAddressEnumerator *enumerator;
358
359   if (strchr (arg, '/'))
360     {
361       /* service/protocol/domain */
362       parts = g_strsplit (arg, "/", 3);
363       if (!parts || !parts[2])
364         usage ();
365
366       connectable = g_network_service_new (parts[0], parts[1], parts[2]);
367     }
368   else
369     {
370       guint16 port;
371
372       parts = g_strsplit (arg, ":", 2);
373       if (parts && parts[1])
374         {
375           arg = parts[0];
376           port = strtoul (parts[1], NULL, 10);
377         }
378       else
379         port = 0;
380
381       if (g_hostname_is_ip_address (arg))
382         {
383           GInetAddress *addr = g_inet_address_new_from_string (arg);
384           GSocketAddress *sockaddr = g_inet_socket_address_new (addr, port);
385
386           g_object_unref (addr);
387           connectable = G_SOCKET_CONNECTABLE (sockaddr);
388         }
389       else
390         connectable = g_network_address_new (arg, port);
391     }
392
393   while (count--)
394     {
395       enumerator = g_socket_connectable_enumerate (connectable);
396
397       if (synchronous)
398         do_sync_connectable (enumerator);
399       else
400         do_async_connectable (enumerator);
401     }
402   
403   g_object_unref (connectable);
404 }
405
406 #ifdef G_OS_UNIX
407 static int cancel_fds[2];
408
409 static void
410 interrupted (int sig)
411 {
412   gssize c;
413
414   signal (SIGINT, SIG_DFL);
415   c = write (cancel_fds[1], "x", 1);
416   g_assert_cmpint(c, ==, 1);
417 }
418
419 static gboolean
420 async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancel)
421 {
422   g_cancellable_cancel (cancel);
423   return FALSE;
424 }
425 #endif
426
427 int
428 main (int argc, char **argv)
429 {
430   gboolean synchronous = FALSE;
431   guint connectable_count = 0;
432 #ifdef G_OS_UNIX
433   GIOChannel *chan;
434   guint watch;
435 #endif
436
437   g_type_init ();
438
439   /* FIXME: use GOptionContext */
440   while (argc >= 2 && argv[1][0] == '-')
441     {
442       if (!strcmp (argv[1], "-s"))
443         synchronous = TRUE;
444       else if (!strcmp (argv[1], "-c"))
445         {
446           connectable_count = atoi (argv[2]);
447           argv++;
448           argc--;
449         }
450       else
451         usage ();
452
453       argv++;
454       argc--;
455     }
456
457   if (argc < 2 || (argc > 2 && connectable_count))
458     usage ();
459
460   resolver = g_resolver_get_default ();
461
462   cancellable = g_cancellable_new ();
463
464 #ifdef G_OS_UNIX
465   /* Set up cancellation; we want to cancel if the user ^C's the
466    * program, but we can't cancel directly from an interrupt.
467    */
468   signal (SIGINT, interrupted);
469
470   if (pipe (cancel_fds) == -1)
471     {
472       perror ("pipe");
473       exit (1);
474     }
475   chan = g_io_channel_unix_new (cancel_fds[0]);
476   watch = g_io_add_watch (chan, G_IO_IN, async_cancel, cancellable);
477   g_io_channel_unref (chan);
478 #endif
479
480   nlookups = argc - 1;
481   loop = g_main_loop_new (NULL, TRUE);
482
483   if (connectable_count)
484     {
485       nlookups = connectable_count;
486       do_connectable (argv[1], synchronous, connectable_count);
487     }
488   else
489     {
490       if (synchronous)
491         start_sync_lookups (argv + 1, argc - 1);
492       else
493         start_async_lookups (argv + 1, argc - 1);
494     }
495
496   g_main_loop_run (loop);
497   g_main_loop_unref (loop);
498
499 #ifdef G_OS_UNIX
500   g_source_remove (watch);
501 #endif
502   g_object_unref (cancellable);
503
504   return 0;
505 }