Add g_network_address_parse
[platform/upstream/glib.git] / gio / gnetworkaddress.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 "gnetworkaddress.h"
28 #include "gasyncresult.h"
29 #include "ginetaddress.h"
30 #include "ginetsocketaddress.h"
31 #include "gresolver.h"
32 #include "gsimpleasyncresult.h"
33 #include "gsocketaddressenumerator.h"
34 #include "gioerror.h"
35 #include "gsocketconnectable.h"
36
37 #ifdef HAVE_NETDB_H
38 #include <netdb.h>
39 #endif
40 #ifdef HAVE_WINSOCK2_H
41 #include <winsock2.h>
42 #endif
43
44 #include <string.h>
45
46 #include "gioalias.h"
47
48 /**
49  * SECTION:gnetworkaddress
50  * @short_description: a #GSocketConnectable for resolving hostnames
51  * @include: gio/gio.h
52  *
53  * #GNetworkAddress provides an easy way to resolve a hostname and
54  * then attempt to connect to that host, handling the possibility of
55  * multiple IP addresses and multiple address families.
56  *
57  * See #GSocketConnectable for and example of using the connectable
58  * interface.
59  **/
60
61 /**
62  * GNetworkAddress:
63  *
64  * A #GSocketConnectable for resolving a hostname and connecting to
65  * that host.
66  **/
67
68 struct _GNetworkAddressPrivate {
69   gchar *hostname;
70   guint16 port;
71   GList *sockaddrs;
72 };
73
74 enum {
75   PROP_0,
76   PROP_HOSTNAME,
77   PROP_PORT,
78 };
79
80 static void g_network_address_set_property (GObject      *object,
81                                             guint         prop_id,
82                                             const GValue *value,
83                                             GParamSpec   *pspec);
84 static void g_network_address_get_property (GObject      *object,
85                                             guint         prop_id,
86                                             GValue       *value,
87                                             GParamSpec   *pspec);
88
89 static void                      g_network_address_connectable_iface_init (GSocketConnectableIface *iface);
90 static GSocketAddressEnumerator *g_network_address_connectable_enumerate  (GSocketConnectable      *connectable);
91
92 G_DEFINE_TYPE_WITH_CODE (GNetworkAddress, g_network_address, G_TYPE_OBJECT,
93                          G_IMPLEMENT_INTERFACE (G_TYPE_SOCKET_CONNECTABLE,
94                                                 g_network_address_connectable_iface_init))
95
96 static void
97 g_network_address_finalize (GObject *object)
98 {
99   GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
100
101   g_free (addr->priv->hostname);
102
103   if (addr->priv->sockaddrs)
104     {
105       GList *a;
106
107       for (a = addr->priv->sockaddrs; a; a = a->next)
108         g_object_unref (a->data);
109       g_list_free (addr->priv->sockaddrs);
110     }
111
112   G_OBJECT_CLASS (g_network_address_parent_class)->finalize (object);
113 }
114
115 static void
116 g_network_address_class_init (GNetworkAddressClass *klass)
117 {
118   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
119
120   g_type_class_add_private (klass, sizeof (GNetworkAddressPrivate));
121
122   gobject_class->set_property = g_network_address_set_property;
123   gobject_class->get_property = g_network_address_get_property;
124   gobject_class->finalize = g_network_address_finalize;
125
126   g_object_class_install_property (gobject_class, PROP_HOSTNAME,
127                                    g_param_spec_string ("hostname",
128                                                         P_("Hostname"),
129                                                         P_("Hostname to resolve"),
130                                                         NULL,
131                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
132   g_object_class_install_property (gobject_class, PROP_PORT,
133                                    g_param_spec_uint ("port",
134                                                       P_("Port"),
135                                                       P_("Network port"),
136                                                       0, 65535, 0,
137                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
138 }
139
140 static void
141 g_network_address_connectable_iface_init (GSocketConnectableIface *connectable_iface)
142 {
143   connectable_iface->enumerate  = g_network_address_connectable_enumerate;
144 }
145
146 static void
147 g_network_address_init (GNetworkAddress *addr)
148 {
149   addr->priv = G_TYPE_INSTANCE_GET_PRIVATE (addr, G_TYPE_NETWORK_ADDRESS,
150                                             GNetworkAddressPrivate);
151 }
152
153 static void
154 g_network_address_set_property (GObject      *object,
155                                 guint         prop_id,
156                                 const GValue *value,
157                                 GParamSpec   *pspec)
158 {
159   GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
160
161   switch (prop_id) 
162     {
163     case PROP_HOSTNAME:
164       if (addr->priv->hostname)
165         g_free (addr->priv->hostname);
166       addr->priv->hostname = g_value_dup_string (value);
167       break;
168
169     case PROP_PORT:
170       addr->priv->port = g_value_get_uint (value);
171       break;
172
173     default:
174       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
175       break;
176     }
177
178 }
179
180 static void
181 g_network_address_get_property (GObject    *object,
182                                 guint       prop_id,
183                                 GValue     *value,
184                                 GParamSpec *pspec)
185 {
186   GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
187
188   switch (prop_id)
189     { 
190     case PROP_HOSTNAME:
191       g_value_set_string (value, addr->priv->hostname);
192       break;
193
194     case PROP_PORT:
195       g_value_set_uint (value, addr->priv->port);
196       break;
197
198     default:
199       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
200       break;
201     }
202
203 }
204
205 static void
206 g_network_address_set_addresses (GNetworkAddress *addr,
207                                  GList           *addresses)
208 {
209   GList *a;
210   GSocketAddress *sockaddr;
211
212   g_return_if_fail (addresses != NULL && addr->priv->sockaddrs == NULL);
213
214   for (a = addresses; a; a = a->next)
215     {
216       sockaddr = g_inet_socket_address_new (a->data, addr->priv->port);
217       addr->priv->sockaddrs = g_list_prepend (addr->priv->sockaddrs, sockaddr);
218       g_object_unref (a->data);
219     }
220   g_list_free (addresses);
221   addr->priv->sockaddrs = g_list_reverse (addr->priv->sockaddrs);
222 }
223
224 /**
225  * g_network_address_new:
226  * @hostname: the hostname
227  * @port: the port
228  *
229  * Creates a new #GSocketConnectable for connecting to the given
230  * @hostname and @port.
231  *
232  * Return value: the new #GNetworkAddress
233  *
234  * Since: 2.22
235  **/
236 GSocketConnectable *
237 g_network_address_new (const gchar *hostname,
238                        guint16      port)
239 {
240   return g_object_new (G_TYPE_NETWORK_ADDRESS,
241                        "hostname", hostname,
242                        "port", port,
243                        NULL);
244 }
245
246 /**
247  * g_network_address_parse:
248  * @host_and_port: the hostname and optionally a port
249  * @default_port: the default port if not in @host_and_port
250  * @error: a pointer to a #GError, or %NULL
251  *
252  * Creates a new #GSocketConnectable for connecting to the given
253  * @hostname and @port. May fail and return %NULL in case
254  * parsing @host_and_port fails.
255  *
256  * @host_and_port may be in any of a number of recognised formats: an IPv6
257  * address, an IPv4 address, or a domain name (in which case a DNS
258  * lookup is performed).  Quoting with [] is supported for all address
259  * types.  A port override may be specified in the usual way with a
260  * colon.  Ports may be given as decimal numbers or symbolic names (in
261  * which case an /etc/services lookup is performed).
262  *
263  * If no port is specified in @host_and_port then @default_port will be
264  * used as the port number to connect to.
265  *
266  * In general, @host_and_port is expected to be provided by the user (allowing
267  * them to give the hostname, and a port overide if necessary) and
268  * @default_port is expected to be provided by the application.
269  *
270  * Return value: the new #GNetworkAddress, or %NULL on error
271  *
272  * Since: 2.22
273  **/
274 GSocketConnectable *
275 g_network_address_parse (const char *host_and_port,
276                          guint16 default_port,
277                          GError **error)
278 {
279   GSocketConnectable *connectable;
280   const gchar *port;
281   guint16 portnum;
282   gchar *name;
283
284   g_return_val_if_fail (host_and_port != NULL, NULL);
285
286   port = NULL;
287   if (host_and_port[0] == '[')
288     /* escaped host part (to allow, eg. "[2001:db8::1]:888") */
289     {
290       const gchar *end;
291
292       end = strchr (host_and_port, ']');
293       if (end == NULL)
294         {
295           g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
296                        _("Hostname '%s' contains '[' but not ']'"), host_and_port);
297           return NULL;
298         }
299
300       if (end[1] == '\0')
301         port = NULL;
302       else if (end[1] == ':')
303         port = &end[2];
304       else
305         {
306           g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
307                        "The ']' character (in hostname '%s') must come at the"
308                        " end or be immediately followed by ':' and a port",
309                        host_and_port);
310           return NULL;
311         }
312
313       name = g_strndup (host_and_port + 1, end - host_and_port - 1);
314     }
315
316   else if ((port = strchr (host_and_port, ':')))
317     /* string has a ':' in it */
318     {
319       /* skip ':' */
320       port++;
321
322       if (strchr (port, ':'))
323         /* more than one ':' in string */
324         {
325           /* this is actually an unescaped IPv6 address */
326           name = g_strdup (host_and_port);
327           port = NULL;
328         }
329       else
330         name = g_strndup (host_and_port, port - host_and_port - 1);
331     }
332
333   else
334     /* plain hostname, no port */
335     name = g_strdup (host_and_port);
336
337   if (port != NULL)
338     {
339       if (port[0] == '\0')
340         {
341           g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
342                        "If a ':' character is given, it must be followed by a "
343                        "port (in hostname '%s').", host_and_port);
344           g_free (name);
345           return NULL;
346         }
347
348       else if ('0' <= port[0] && port[0] <= '9')
349         {
350           char *end;
351           long value;
352
353           value = strtol (port, &end, 10);
354           if (*end != '\0' || value < 0 || value > G_MAXUINT16)
355             {
356               g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
357                            "Invalid numeric port '%s' specified in hostname '%s'",
358                            port, host_and_port);
359               g_free (name);
360               return NULL;
361             }
362
363           portnum = value;
364         }
365
366       else
367         {
368           struct servent *entry;
369
370           entry = getservbyname (port, "tcp");
371           if (entry == NULL)
372             {
373               g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
374                            "Unknown service '%s' specified in hostname '%s'",
375                            port, host_and_port);
376 #ifdef HAVE_ENDSERVENT
377               endservent ();
378 #endif
379               g_free (name);
380               return NULL;
381             }
382
383           portnum = g_ntohs (entry->s_port);
384
385 #ifdef HAVE_ENDSERVENT
386           endservent ();
387 #endif
388         }
389     }
390   else
391     {
392       /* No port in host_and_port */
393       portnum = default_port;
394     }
395
396   connectable = g_network_address_new (name, portnum);
397   g_free (name);
398
399   return connectable;
400 }
401
402 /**
403  * g_network_address_get_hostname:
404  * @addr: a #GNetworkAddress
405  *
406  * Gets @addr's hostname. This might be either UTF-8 or ASCII-encoded,
407  * depending on what @addr was created with.
408  *
409  * Return value: @addr's hostname
410  *
411  * Since: 2.22
412  **/
413 const gchar *
414 g_network_address_get_hostname (GNetworkAddress *addr)
415 {
416   g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), NULL);
417
418   return addr->priv->hostname;
419 }
420
421 /**
422  * g_network_address_get_port:
423  * @addr: a #GNetworkAddress
424  *
425  * Gets @addr's port number
426  *
427  * Return value: @addr's port (which may be %0)
428  *
429  * Since: 2.22
430  **/
431 guint16
432 g_network_address_get_port (GNetworkAddress *addr)
433 {
434   g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), 0);
435
436   return addr->priv->port;
437 }
438
439 #define G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (_g_network_address_address_enumerator_get_type ())
440 #define G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR, GNetworkAddressAddressEnumerator))
441
442 typedef struct {
443   GSocketAddressEnumerator parent_instance;
444
445   GNetworkAddress *addr;
446   GList *a;
447 } GNetworkAddressAddressEnumerator;
448
449 typedef struct {
450   GSocketAddressEnumeratorClass parent_class;
451
452 } GNetworkAddressAddressEnumeratorClass;
453
454 G_DEFINE_TYPE (GNetworkAddressAddressEnumerator, _g_network_address_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
455
456 static void
457 g_network_address_address_enumerator_finalize (GObject *object)
458 {
459   GNetworkAddressAddressEnumerator *addr_enum =
460     G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (object);
461
462   g_object_unref (addr_enum->addr);
463
464   G_OBJECT_CLASS (_g_network_address_address_enumerator_parent_class)->finalize (object);
465 }
466
467 static GSocketAddress *
468 g_network_address_address_enumerator_next (GSocketAddressEnumerator  *enumerator,
469                                            GCancellable              *cancellable,
470                                            GError                   **error)
471 {
472   GNetworkAddressAddressEnumerator *addr_enum =
473     G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
474   GSocketAddress *sockaddr;
475
476   if (!addr_enum->addr->priv->sockaddrs)
477     {
478       GResolver *resolver = g_resolver_get_default ();
479       GList *addresses;
480
481       addresses = g_resolver_lookup_by_name (resolver,
482                                              addr_enum->addr->priv->hostname,
483                                              cancellable, error);
484       g_object_unref (resolver);
485
486       if (!addresses)
487         return NULL;
488
489       g_network_address_set_addresses (addr_enum->addr, addresses);
490       addr_enum->a = addr_enum->addr->priv->sockaddrs;
491     }
492
493   if (!addr_enum->a)
494     return NULL;
495   else
496     {
497       sockaddr = addr_enum->a->data;
498       addr_enum->a = addr_enum->a->next;
499       return g_object_ref (sockaddr);
500     }
501 }
502
503 static void
504 got_addresses (GObject      *source_object,
505                GAsyncResult *result,
506                gpointer      user_data)
507 {
508   GSimpleAsyncResult *simple = user_data;
509   GNetworkAddressAddressEnumerator *addr_enum =
510     g_simple_async_result_get_op_res_gpointer (simple);
511   GResolver *resolver = G_RESOLVER (source_object);
512   GList *addresses;
513   GError *error = NULL;
514
515   addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
516   if (!addr_enum->addr->priv->sockaddrs)
517     {
518       if (error)
519         {
520           g_simple_async_result_set_from_error (simple, error);
521           g_error_free (error);
522         }
523       else
524         {
525           g_network_address_set_addresses (addr_enum->addr, addresses);
526           addr_enum->a = addr_enum->addr->priv->sockaddrs;
527         }
528     }
529   else if (error)
530     g_error_free (error);
531
532   g_object_unref (resolver);
533
534   g_simple_async_result_complete (simple);
535   g_object_unref (simple);
536 }
537
538 static void
539 g_network_address_address_enumerator_next_async (GSocketAddressEnumerator  *enumerator,
540                                                  GCancellable              *cancellable,
541                                                  GAsyncReadyCallback        callback,
542                                                  gpointer                   user_data)
543 {
544   GNetworkAddressAddressEnumerator *addr_enum =
545     G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
546   GSimpleAsyncResult *simple;
547   GSocketAddress *sockaddr;
548
549   simple = g_simple_async_result_new (G_OBJECT (enumerator),
550                                       callback, user_data,
551                                       g_network_address_address_enumerator_next_async);
552
553   if (!addr_enum->addr->priv->sockaddrs)
554     {
555       GResolver *resolver = g_resolver_get_default ();
556
557       g_simple_async_result_set_op_res_gpointer (simple, g_object_ref (addr_enum), g_object_unref);
558       g_resolver_lookup_by_name_async (resolver,
559                                        addr_enum->addr->priv->hostname,
560                                        cancellable,
561                                        got_addresses, simple);
562     }
563   else
564     {
565       sockaddr = g_network_address_address_enumerator_next (enumerator, NULL, NULL);
566       if (sockaddr)
567         g_simple_async_result_set_op_res_gpointer (simple, sockaddr, g_object_unref);
568
569       g_simple_async_result_complete_in_idle (simple);
570       g_object_unref (simple);
571     }
572 }
573
574 static GSocketAddress *
575 g_network_address_address_enumerator_next_finish (GSocketAddressEnumerator  *enumerator,
576                                                   GAsyncResult              *result,
577                                                   GError                   **error)
578 {
579   GNetworkAddressAddressEnumerator *addr_enum =
580     G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
581   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
582   GSocketAddress *sockaddr;
583
584   if (g_simple_async_result_propagate_error (simple, error))
585     return NULL;
586   else if (!addr_enum->a)
587     return NULL;
588   else
589     {
590       sockaddr = addr_enum->a->data;
591       addr_enum->a = addr_enum->a->next;
592       return g_object_ref (sockaddr);
593     }
594 }
595
596 static void
597 _g_network_address_address_enumerator_init (GNetworkAddressAddressEnumerator *enumerator)
598 {
599 }
600
601 static void
602 _g_network_address_address_enumerator_class_init (GNetworkAddressAddressEnumeratorClass *addrenum_class)
603 {
604   GObjectClass *object_class = G_OBJECT_CLASS (addrenum_class);
605   GSocketAddressEnumeratorClass *enumerator_class =
606     G_SOCKET_ADDRESS_ENUMERATOR_CLASS (addrenum_class);
607
608   enumerator_class->next = g_network_address_address_enumerator_next;
609   enumerator_class->next_async = g_network_address_address_enumerator_next_async;
610   enumerator_class->next_finish = g_network_address_address_enumerator_next_finish;
611   object_class->finalize = g_network_address_address_enumerator_finalize;
612 }
613
614 static GSocketAddressEnumerator *
615 g_network_address_connectable_enumerate (GSocketConnectable *connectable)
616 {
617   GNetworkAddressAddressEnumerator *addr_enum;
618
619   addr_enum = g_object_new (G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR, NULL);
620   addr_enum->addr = g_object_ref (connectable);
621
622   return (GSocketAddressEnumerator *)addr_enum;
623 }
624
625 #define __G_NETWORK_ADDRESS_C__
626 #include "gioaliasdef.c"