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