Tizen 2.1 base
[platform/upstream/glib2.0.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     {
205       GThread *thread;
206       thread = g_thread_new ("lookup", lookup_thread, argv[i]);
207       g_thread_unref (thread);
208     }
209 }
210
211 static void
212 lookup_by_addr_callback (GObject *source, GAsyncResult *result,
213                          gpointer user_data)
214 {
215   const char *phys = user_data;
216   GError *error = NULL;
217   char *name;
218
219   name = g_resolver_lookup_by_address_finish (resolver, result, &error);
220   print_resolved_name (phys, name, error);
221 }
222
223 static void
224 lookup_by_name_callback (GObject *source, GAsyncResult *result,
225                          gpointer user_data)
226 {
227   const char *name = user_data;
228   GError *error = NULL;
229   GList *addresses;
230
231   addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
232   print_resolved_addresses (name, addresses, error);
233 }
234
235 static void
236 lookup_service_callback (GObject *source, GAsyncResult *result,
237                          gpointer user_data)
238 {
239   const char *service = user_data;
240   GError *error = NULL;
241   GList *targets;
242
243   targets = g_resolver_lookup_service_finish (resolver, result, &error);
244   print_resolved_service (service, targets, error);
245 }
246
247 static void
248 start_async_lookups (char **argv, int argc)
249 {
250   int i;
251
252   for (i = 0; i < argc; i++)
253     {
254       if (strchr (argv[i], '/'))
255         {
256           /* service/protocol/domain */
257           char **parts = g_strsplit (argv[i], "/", 3);
258
259           if (!parts || !parts[2])
260             usage ();
261
262           g_resolver_lookup_service_async (resolver,
263                                            parts[0], parts[1], parts[2],
264                                            cancellable,
265                                            lookup_service_callback, argv[i]);
266         }
267       else if (g_hostname_is_ip_address (argv[i]))
268         {
269           GInetAddress *addr = g_inet_address_new_from_string (argv[i]);
270
271           g_resolver_lookup_by_address_async (resolver, addr, cancellable,
272                                               lookup_by_addr_callback, argv[i]);
273           g_object_unref (addr);
274         }
275       else
276         {
277           g_resolver_lookup_by_name_async (resolver, argv[i], cancellable,
278                                            lookup_by_name_callback,
279                                            argv[i]);
280         }
281
282       /* Stress-test the reloading code */
283       g_signal_emit_by_name (resolver, "reload");
284     }
285 }
286
287 static void
288 print_connectable_sockaddr (GSocketAddress *sockaddr,
289                             GError         *error)
290 {
291   char *phys;
292
293   if (error)
294     {
295       printf ("Error:   %s\n", error->message);
296       g_error_free (error);
297     }
298   else if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
299     {
300       printf ("Error:   Unexpected sockaddr type '%s'\n", g_type_name_from_instance ((GTypeInstance *)sockaddr));
301       g_object_unref (sockaddr);
302     }
303   else
304     {
305       GInetSocketAddress *isa = G_INET_SOCKET_ADDRESS (sockaddr);
306       phys = g_inet_address_to_string (g_inet_socket_address_get_address (isa));
307       printf ("Address: %s%s%s:%d\n",
308               strchr (phys, ':') ? "[" : "", phys, strchr (phys, ':') ? "]" : "",
309               g_inet_socket_address_get_port (isa));
310       g_free (phys);
311       g_object_unref (sockaddr);
312     }
313 }
314
315 static void
316 do_sync_connectable (GSocketAddressEnumerator *enumerator)
317 {
318   GSocketAddress *sockaddr;
319   GError *error = NULL;
320
321   while ((sockaddr = g_socket_address_enumerator_next (enumerator, cancellable, &error)))
322     print_connectable_sockaddr (sockaddr, error);
323
324   g_object_unref (enumerator);
325   done_lookup ();
326 }
327
328 static void do_async_connectable (GSocketAddressEnumerator *enumerator);
329
330 static void
331 got_next_async (GObject *source, GAsyncResult *result, gpointer user_data)
332 {
333   GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (source);
334   GSocketAddress *sockaddr;
335   GError *error = NULL;
336
337   sockaddr = g_socket_address_enumerator_next_finish (enumerator, result, &error);
338   if (sockaddr || error)
339     print_connectable_sockaddr (sockaddr, error);
340   if (sockaddr)
341     do_async_connectable (enumerator);
342   else
343     {
344       g_object_unref (enumerator);
345       done_lookup ();
346     }
347 }
348
349 static void
350 do_async_connectable (GSocketAddressEnumerator *enumerator)
351 {
352   g_socket_address_enumerator_next_async (enumerator, cancellable,
353                                           got_next_async, NULL);
354 }
355
356 static void
357 do_connectable (const char *arg, gboolean synchronous, guint count)
358 {
359   char **parts;
360   GSocketConnectable *connectable;
361   GSocketAddressEnumerator *enumerator;
362
363   if (strchr (arg, '/'))
364     {
365       /* service/protocol/domain */
366       parts = g_strsplit (arg, "/", 3);
367       if (!parts || !parts[2])
368         usage ();
369
370       connectable = g_network_service_new (parts[0], parts[1], parts[2]);
371     }
372   else
373     {
374       guint16 port;
375
376       parts = g_strsplit (arg, ":", 2);
377       if (parts && parts[1])
378         {
379           arg = parts[0];
380           port = strtoul (parts[1], NULL, 10);
381         }
382       else
383         port = 0;
384
385       if (g_hostname_is_ip_address (arg))
386         {
387           GInetAddress *addr = g_inet_address_new_from_string (arg);
388           GSocketAddress *sockaddr = g_inet_socket_address_new (addr, port);
389
390           g_object_unref (addr);
391           connectable = G_SOCKET_CONNECTABLE (sockaddr);
392         }
393       else
394         connectable = g_network_address_new (arg, port);
395     }
396
397   while (count--)
398     {
399       enumerator = g_socket_connectable_enumerate (connectable);
400
401       if (synchronous)
402         do_sync_connectable (enumerator);
403       else
404         do_async_connectable (enumerator);
405     }
406   
407   g_object_unref (connectable);
408 }
409
410 #ifdef G_OS_UNIX
411 static int cancel_fds[2];
412
413 static void
414 interrupted (int sig)
415 {
416   gssize c;
417
418   signal (SIGINT, SIG_DFL);
419   c = write (cancel_fds[1], "x", 1);
420   g_assert_cmpint(c, ==, 1);
421 }
422
423 static gboolean
424 async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancel)
425 {
426   g_cancellable_cancel (cancel);
427   return FALSE;
428 }
429 #endif
430
431 int
432 main (int argc, char **argv)
433 {
434   gboolean synchronous = FALSE;
435   guint connectable_count = 0;
436 #ifdef G_OS_UNIX
437   GIOChannel *chan;
438   guint watch;
439 #endif
440
441   g_type_init ();
442
443   /* FIXME: use GOptionContext */
444   while (argc >= 2 && argv[1][0] == '-')
445     {
446       if (!strcmp (argv[1], "-s"))
447         synchronous = TRUE;
448       else if (!strcmp (argv[1], "-c"))
449         {
450           connectable_count = atoi (argv[2]);
451           argv++;
452           argc--;
453         }
454       else
455         usage ();
456
457       argv++;
458       argc--;
459     }
460
461   if (argc < 2 || (argc > 2 && connectable_count))
462     usage ();
463
464   resolver = g_resolver_get_default ();
465
466   cancellable = g_cancellable_new ();
467
468 #ifdef G_OS_UNIX
469   /* Set up cancellation; we want to cancel if the user ^C's the
470    * program, but we can't cancel directly from an interrupt.
471    */
472   signal (SIGINT, interrupted);
473
474   if (pipe (cancel_fds) == -1)
475     {
476       perror ("pipe");
477       exit (1);
478     }
479   chan = g_io_channel_unix_new (cancel_fds[0]);
480   watch = g_io_add_watch (chan, G_IO_IN, async_cancel, cancellable);
481   g_io_channel_unref (chan);
482 #endif
483
484   nlookups = argc - 1;
485   loop = g_main_loop_new (NULL, TRUE);
486
487   if (connectable_count)
488     {
489       nlookups = connectable_count;
490       do_connectable (argv[1], synchronous, connectable_count);
491     }
492   else
493     {
494       if (synchronous)
495         start_sync_lookups (argv + 1, argc - 1);
496       else
497         start_async_lookups (argv + 1, argc - 1);
498     }
499
500   g_main_loop_run (loop);
501   g_main_loop_unref (loop);
502
503 #ifdef G_OS_UNIX
504   g_source_remove (watch);
505 #endif
506   g_object_unref (cancellable);
507
508   return 0;
509 }