2
* gibber-resolver.c - Source for GibberResolver
3
* Copyright (C) 2008 Collabora Ltd.
4
* @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
6
* This library is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Lesser General Public
8
* License as published by the Free Software Foundation; either
9
* version 2.1 of the License, or (at your option) any later version.
11
* This library is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with this library; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
#include <netinet/in.h>
26
#include <arpa/nameser.h>
28
#include <sys/types.h>
34
#include "gibber-resolver.h"
36
#ifdef HAVE_LIBASYNCNS
37
#include "gibber-resolver-asyncns.h"
40
static GibberResolver *resolver_singleton = NULL;
41
static GType resolver_singleton_type = 0;
44
gibber_resolver_get_resolver (void)
47
if (resolver_singleton_type == 0)
48
#ifdef HAVE_LIBASYNCNS
49
resolver_singleton_type = GIBBER_TYPE_RESOLVER_ASYNCNS;
51
resolver_singleton_type = GIBBER_TYPE_RESOLVER;
54
if (resolver_singleton == NULL)
55
resolver_singleton = g_object_new (resolver_singleton_type, NULL);
57
return resolver_singleton;
61
gibber_resolver_set_resolver (GType object_type)
63
if (resolver_singleton_type != object_type && resolver_singleton != NULL)
65
g_object_unref (resolver_singleton);
66
resolver_singleton = NULL;
69
resolver_singleton_type = object_type;
74
G_DEFINE_TYPE(GibberResolver, gibber_resolver, G_TYPE_OBJECT)
78
GibberResolver *resolver;
80
/* Data the user would like us to remember */
82
GDestroyNotify destroy;
86
/* Field settable by implementations of GibberResolver */
90
/* private structure */
91
typedef struct _GibberResolverPrivate GibberResolverPrivate;
93
struct _GibberResolverPrivate
95
gboolean dispose_has_run;
96
/* guint * -> GibberResolverJob struct */
100
static gboolean resolver_resolv_srv (GibberResolver *resolver, guint id,
101
const gchar *service_name, const char *service,
102
GibberResolverServiceType type);
104
static gboolean resolver_resolv_addrinfo (GibberResolver *resolver, guint id,
105
const gchar *hostname, const char *port, int address_family, int sock_type,
106
int protocol, int flags);
108
static gboolean resolver_resolv_nameinfo (GibberResolver *resolver, guint id,
109
const struct sockaddr *sa, socklen_t salen, gint flags);
111
static void resolver_resolv_cancel (GibberResolver *resolver, guint id);
113
static void free_job (gpointer data);
115
#define GIBBER_RESOLVER_GET_PRIVATE(o) \
116
(G_TYPE_INSTANCE_GET_PRIVATE ((o), GIBBER_TYPE_RESOLVER, \
117
GibberResolverPrivate))
120
gibber_resolver_error_quark (void)
122
static GQuark quark = 0;
125
quark = g_quark_from_static_string ("gibber_resolver_error");
131
gibber_resolver_init (GibberResolver *obj)
133
GibberResolverPrivate *priv = GIBBER_RESOLVER_GET_PRIVATE (obj);
135
/* allocate any data required by the object here */
136
priv->jobs = g_hash_table_new_full (g_int_hash, g_int_equal,
140
static void gibber_resolver_dispose (GObject *object);
141
static void gibber_resolver_finalize (GObject *object);
144
gibber_resolver_class_init (GibberResolverClass *gibber_resolver_class)
146
GObjectClass *object_class = G_OBJECT_CLASS (gibber_resolver_class);
148
g_type_class_add_private (gibber_resolver_class,
149
sizeof (GibberResolverPrivate));
151
object_class->dispose = gibber_resolver_dispose;
152
object_class->finalize = gibber_resolver_finalize;
154
gibber_resolver_class->resolv_srv = resolver_resolv_srv;
155
gibber_resolver_class->resolv_addrinfo = resolver_resolv_addrinfo;
156
gibber_resolver_class->resolv_nameinfo = resolver_resolv_nameinfo;
157
gibber_resolver_class->resolv_cancel = resolver_resolv_cancel;
161
gibber_resolver_dispose (GObject *object)
163
GibberResolver *self = GIBBER_RESOLVER (object);
164
GibberResolverPrivate *priv = GIBBER_RESOLVER_GET_PRIVATE (self);
166
if (priv->dispose_has_run)
169
priv->dispose_has_run = TRUE;
171
if (priv->jobs != NULL)
172
g_hash_table_destroy (priv->jobs);
175
/* release any references held by the object here */
176
if (G_OBJECT_CLASS (gibber_resolver_parent_class)->dispose)
177
G_OBJECT_CLASS (gibber_resolver_parent_class)->dispose (object);
181
gibber_resolver_finalize (GObject *object)
184
/* free any data held directly by the object here */
186
G_OBJECT_CLASS (gibber_resolver_parent_class)->finalize (object);
190
weak_object_destroyed (gpointer data, GObject *old_object)
192
GibberResolverJob *job = (GibberResolverJob *) data;
194
g_assert (job->weak_object == old_object);
196
job->weak_object = NULL;
198
gibber_resolver_cancel (job->resolver, job->jobid);
202
gibber_resolver_job_add (GibberResolver *resolver,
205
GDestroyNotify destroy,
206
GObject *weak_object)
208
GibberResolverPrivate *priv = GIBBER_RESOLVER_GET_PRIVATE (resolver);
209
GibberResolverJob *job;
211
job = g_slice_new0 (GibberResolverJob);
212
job->resolver = g_object_ref (resolver);
214
job->callback = callback;
215
job->destroy = destroy;
216
job->user_data = user_data;
217
job->weak_object = weak_object;
219
/* Now decide on a decent job id.. The pointer is a pretty good initial
220
* guess. A nicer solution would be to use an intset */
221
job->jobid = GPOINTER_TO_UINT (job);
223
/* Be carefull to skip 0 */
224
while (job->jobid == 0 ||
225
g_hash_table_lookup (priv->jobs, &(job->jobid)) != NULL)
228
g_hash_table_insert (priv->jobs, &(job->jobid), job);
230
if (weak_object != NULL)
232
g_object_weak_ref (weak_object, weak_object_destroyed, job);
238
static void free_job (gpointer data)
240
GibberResolverJob *job = (GibberResolverJob *)data;
243
job->destroy (job->user_data);
245
if (job->weak_object)
246
g_object_weak_unref (job->weak_object, weak_object_destroyed, job);
248
g_object_unref (job->resolver);
249
g_slice_free (GibberResolverJob, job);
252
GibberResolverAddrInfo *
253
gibber_resolver_addrinfo_new (gint address_family,
256
struct sockaddr *addr,
259
GibberResolverAddrInfo *result;
261
result = g_slice_new (GibberResolverAddrInfo);
263
result->address_family = address_family;
264
result->socket_type = socket_type;
265
result->protocol = protocol;
266
memcpy (&(result->sockaddr), addr, sockaddr_len);
267
result->sockaddr_len = sockaddr_len;
273
gibber_resolver_addrinfo_free (GibberResolverAddrInfo *addrinfo)
275
g_slice_free (GibberResolverAddrInfo, addrinfo);
279
gibber_resolver_addrinfo_list_free (GList *addrinfo_list)
282
GibberResolverAddrInfo *a;
284
for (t = addrinfo_list ; t != NULL; t = g_list_delete_link (t, t))
286
a = (GibberResolverAddrInfo *)t->data;
287
gibber_resolver_addrinfo_free (a);
291
GibberResolverSrvRecord *
292
gibber_resolver_srv_record_new (gchar *hostname,
297
GibberResolverSrvRecord *result;
299
result = g_slice_new (GibberResolverSrvRecord);
300
result->hostname = g_strdup (hostname);
302
result->priority = priority;
303
result->weight = weight;
309
gibber_resolver_srv_free (GibberResolverSrvRecord *srvrecord)
311
g_free (srvrecord->hostname);
312
g_slice_free (GibberResolverSrvRecord, srvrecord);
316
gibber_resolver_srv_list_free (GList *srv_list)
319
GibberResolverSrvRecord *s;
321
for (t = srv_list ; t != NULL; t = g_list_delete_link (t, t))
323
s = (GibberResolverSrvRecord *) t->data;
324
gibber_resolver_srv_free (s);
330
gibber_resolver_srv (GibberResolver *resolver,
331
const gchar *service_name,
333
GibberResolverServiceType type,
334
gibber_resolver_srv_cb callback,
336
GDestroyNotify destroy,
337
GObject *weak_object)
339
GibberResolverClass *cls = GIBBER_RESOLVER_GET_CLASS (resolver);
343
jobid = gibber_resolver_job_add (resolver, G_CALLBACK (callback), user_data,
344
destroy, weak_object);
346
ret = cls->resolv_srv (resolver, jobid, service_name, service, type);
348
return ret ? jobid : 0;
352
gibber_resolver_addrinfo (GibberResolver *resolver,
353
const gchar *hostname,
359
gibber_resolver_addrinfo_cb callback,
361
GDestroyNotify destroy,
362
GObject *weak_object)
364
GibberResolverClass *cls = GIBBER_RESOLVER_GET_CLASS (resolver);
368
jobid = gibber_resolver_job_add (resolver, G_CALLBACK (callback),
369
user_data, destroy, weak_object);
371
ret = cls->resolv_addrinfo (resolver, jobid, hostname, port, address_family,
372
sock_type, protocol, flags);
374
return ret ? jobid : 0;
378
gibber_resolver_nameinfo (GibberResolver *resolver,
379
const struct sockaddr *sa,
382
gibber_resolver_nameinfo_cb callback,
384
GDestroyNotify destroy,
385
GObject *weak_object)
387
GibberResolverClass *cls = GIBBER_RESOLVER_GET_CLASS (resolver);
391
jobid = gibber_resolver_job_add (resolver, G_CALLBACK (callback), user_data,
392
destroy, weak_object);
394
ret = cls->resolv_nameinfo (resolver, jobid, sa, salen, flags);
396
return ret ? jobid : 0;
400
gibber_resolver_cancel (GibberResolver *resolver, guint id)
402
GibberResolverClass *cls = GIBBER_RESOLVER_GET_CLASS (resolver);
403
GibberResolverPrivate *priv = GIBBER_RESOLVER_GET_PRIVATE (resolver);
405
if (g_hash_table_lookup (priv->jobs, &id) == NULL)
407
g_warning ("Trying to cancel a non-existing resolver jobs");
411
cls->resolv_cancel (resolver, id);
412
g_hash_table_remove (priv->jobs, &id);
416
gibber_resolver_sockaddr_to_str (const struct sockaddr *sa,
423
gchar name[NI_MAXHOST], servicename[NI_MAXSERV];
425
ret = getnameinfo (sa, salen, name, NI_MAXHOST, servicename, NI_MAXSERV,
426
NI_NUMERICHOST | NI_NUMERICSERV);
430
g_set_error (error, GIBBER_RESOLVER_ERROR, ret,
431
"getnameinfo failed: %s", gai_strerror (ret));
436
*address = g_strdup (name);
439
*service = g_strdup (servicename);
444
/* Utility function for classed implementing GibberResolver */
446
gibber_resolver_set_data (GibberResolver *resolver, guint id, gpointer data)
448
GibberResolverPrivate *priv = GIBBER_RESOLVER_GET_PRIVATE (resolver);
449
GibberResolverJob *job;
451
job = g_hash_table_lookup (priv->jobs, &id);
453
g_assert (job != NULL);
459
gibber_resolver_get_data (GibberResolver *resolver, guint id)
461
GibberResolverPrivate *priv = GIBBER_RESOLVER_GET_PRIVATE (resolver);
462
GibberResolverJob *job;
464
job = g_hash_table_lookup (priv->jobs, &id);
466
g_assert (job != NULL);
472
compare_srv_record (gconstpointer a, gconstpointer b)
474
GibberResolverSrvRecord *asrv = (GibberResolverSrvRecord *) a;
475
GibberResolverSrvRecord *bsrv = (GibberResolverSrvRecord *) b;
477
if (asrv->priority != bsrv->priority)
478
return asrv->priority < bsrv->priority ? -1 : 1;
480
if (asrv->weight != 0 || bsrv->weight != 0)
481
return asrv->weight == 0 ? -1 : 1;
488
weight_sort_srv_list_total (GList *srv_list, gint total)
492
GibberResolverSrvRecord *srv;
494
if (srv_list == NULL)
497
num = g_random_int_range (0, total + 1);
499
for (l = srv_list ; l != NULL; l = g_list_next (l))
501
srv = (GibberResolverSrvRecord *) l->data;
507
g_assert (l != NULL);
509
s = g_list_remove_link (srv_list, l);
511
return g_list_concat (l,
512
weight_sort_srv_list_total (s, total - srv->weight));
516
weight_sort_srv_list (GList *srv_list)
520
GibberResolverSrvRecord *srv;
522
/* Sort srv list of equal priority but with weight as specified in RFC2782 */
523
srv = (GibberResolverSrvRecord *) srv_list->data;
525
g_assert (srv_list != NULL);
527
for (l = srv_list; l != NULL; l = g_list_next (l))
529
srv = (GibberResolverSrvRecord *) l->data;
530
total += srv->weight;
533
return weight_sort_srv_list_total (srv_list, total);
537
cut_list (GList *link)
539
if (link->prev != NULL)
540
link->prev->next = NULL;
545
sort_srv_list (GList *srv_list)
547
GList *result = NULL;
550
guint16 priority = 0;
552
sorted = g_list_sort (srv_list, compare_srv_record);
554
while (sorted != NULL)
558
/* Find the start entry with a non-zero weight */
559
for (start = sorted ; start != NULL &&
560
((GibberResolverSrvRecord *)start->data)->weight == 0;
565
result = g_list_concat (result, sorted);
570
priority = ((GibberResolverSrvRecord *)start->data)->priority;
573
for (end = start ; end != NULL &&
574
((GibberResolverSrvRecord *)end->data)->priority == priority;
585
/* We know have a sublist of entries with the same priority but
586
* different weights */
587
start = weight_sort_srv_list (start);
588
result = g_list_concat (result, start);
596
gibber_resolver_srv_result (GibberResolver *resolver,
598
GList *srv_list, GError *error)
600
GibberResolverPrivate *priv = GIBBER_RESOLVER_GET_PRIVATE (resolver);
601
GibberResolverJob *job;
602
gibber_resolver_srv_cb callback;
604
job = g_hash_table_lookup (priv->jobs, &jobid);
606
g_assert (job != NULL);
608
srv_list = sort_srv_list (srv_list);
610
callback = (gibber_resolver_srv_cb) job->callback;
611
callback (resolver, srv_list, error, job->user_data, job->weak_object);
613
g_hash_table_remove (priv->jobs, &jobid);
617
gibber_resolver_addrinfo_result (GibberResolver *resolver,
622
GibberResolverPrivate *priv = GIBBER_RESOLVER_GET_PRIVATE (resolver);
623
GibberResolverJob *job;
624
gibber_resolver_addrinfo_cb callback;
626
job = g_hash_table_lookup (priv->jobs, &jobid);
628
g_assert (job != NULL);
630
callback = (gibber_resolver_addrinfo_cb)job->callback;
631
callback (resolver, entries, error, job->user_data, job->weak_object);
633
g_hash_table_remove (priv->jobs, &jobid);
637
gibber_resolver_nameinfo_result (GibberResolver *resolver,
639
const gchar *hostname,
643
GibberResolverPrivate *priv = GIBBER_RESOLVER_GET_PRIVATE (resolver);
644
GibberResolverJob *job;
645
gibber_resolver_nameinfo_cb callback;
647
job = g_hash_table_lookup (priv->jobs, &jobid);
649
g_assert (job != NULL);
651
callback = (gibber_resolver_nameinfo_cb) job->callback;
652
callback (resolver, hostname, port, error,
653
job->user_data, job->weak_object);
655
g_hash_table_remove (priv->jobs, &jobid);
659
#define ANSWER_BUFSIZE 10240
661
gibber_resolver_res_query_to_list (guchar *answer, int length)
667
const unsigned char *pos = answer + sizeof (HEADER);
668
unsigned char *end = answer + length;
669
HEADER *head = (HEADER *)answer;
672
qdcount = ntohs (head->qdcount);
673
ancount = ntohs (head->ancount);
675
/* Ignore the questions */
676
while (qdcount-- > 0 && (len = dn_expand (answer, end, pos, name, 255)) >= 0)
678
pos += len + QFIXEDSZ;
681
/* Parse the answers */
683
&& (len = dn_expand (answer, end, pos, name, 255)) >= 0)
685
uint16_t pref, weight, port, class, type;
687
/* Ignore the initial string, which has the query in it */
689
NS_GET16 (type, pos);
690
NS_GET16 (class, pos);
692
if (type != T_SRV || class != C_IN)
695
/* skip ttl and dlen */
698
NS_GET16 (pref, pos);
699
NS_GET16 (weight, pos);
700
NS_GET16 (port, pos);
701
len = dn_expand (answer, end, pos, name, 255);
703
list = g_list_prepend (list,
704
gibber_resolver_srv_record_new (name, port, pref, weight));
712
gibber_resolver_srv_list_free (list);
717
gibber_resolver_gai_error_to_g_error (int error)
726
code = GIBBER_RESOLVER_ERROR_INVALID_ARGUMENT;
730
code = GIBBER_RESOLVER_ERROR_RESOLVE_TEMPORARY_FAILURE;
734
code = GIBBER_RESOLVER_ERROR_RESOLVE_FAILURE;
739
code = GIBBER_RESOLVER_ERROR_MEMORY;
744
code = GIBBER_RESOLVER_ERROR_UNKNOWN;
747
return g_error_new (GIBBER_RESOLVER_ERROR, code, gai_strerror (error));
751
gibber_resolver_h_error_to_g_error (int error)
758
code = GIBBER_RESOLVER_ERROR_RESOLVE_FAILURE,
759
message = "Non-recoverable error";
762
code = GIBBER_RESOLVER_ERROR_RESOLVE_FAILURE,
763
message = "Authoritative Answer Host not found";
766
code = GIBBER_RESOLVER_ERROR_RESOLVE_FAILURE;
767
message = "Valid name, no data record of requested type.";
770
code = GIBBER_RESOLVER_ERROR_RESOLVE_TEMPORARY_FAILURE,
771
message = "Temporary resolver failure";
774
code = GIBBER_RESOLVER_ERROR_UNKNOWN;
775
message = "Unknown error";
778
return g_error_new (GIBBER_RESOLVER_ERROR, code, message);
782
/* Default GibberResolver implementation (blocking) */
784
resolver_resolv_srv (GibberResolver *resolver,
786
const gchar *service_name,
788
GibberResolverServiceType type)
792
GList *entries = NULL;
793
GError *error = NULL;
794
guchar answer[ANSWER_BUFSIZE];
796
srv_str = g_strdup_printf ("_%s._%s.%s", service,
797
type == GIBBER_RESOLVER_SERVICE_TYPE_TCP ? "tcp" : "udp", service_name);
799
ret = res_query (srv_str, C_IN, T_SRV, answer, ANSWER_BUFSIZE);
802
error = gibber_resolver_h_error_to_g_error (h_errno);
805
entries = gibber_resolver_res_query_to_list (answer, ret);
807
error = g_error_new (GIBBER_RESOLVER_ERROR,
808
GIBBER_RESOLVER_ERROR_RESOLVE_FAILURE, "Invalid reply received");
811
gibber_resolver_srv_result (resolver, id, entries, error);
814
g_error_free (error);
822
resolver_resolv_addrinfo (GibberResolver *resolver,
824
const gchar *hostname,
831
struct addrinfo req, *ans = NULL, *tmpaddr;
833
GList *entries = NULL;
835
memset (&req, 0, sizeof (req));
836
req.ai_family = address_family;
837
req.ai_socktype = sock_type;
838
req.ai_protocol = protocol;
839
req.ai_flags = flags;
841
ret = getaddrinfo (hostname, port, &req, &ans);
845
GError *e = gibber_resolver_gai_error_to_g_error (ret);
846
gibber_resolver_addrinfo_result (resolver, id, NULL, e);
851
for (tmpaddr = ans; tmpaddr != NULL; tmpaddr = tmpaddr->ai_next)
853
entries = g_list_append (entries,
854
gibber_resolver_addrinfo_new (tmpaddr->ai_family,
855
tmpaddr->ai_socktype, tmpaddr->ai_protocol,
856
tmpaddr->ai_addr, tmpaddr->ai_addrlen));
861
gibber_resolver_addrinfo_result (resolver, id, entries, NULL);
867
resolver_resolv_nameinfo (GibberResolver *resolver,
869
const struct sockaddr *sa,
874
gchar name[NI_MAXHOST], servicename[NI_MAXSERV];
876
ret = getnameinfo (sa, salen, name, NI_MAXHOST, servicename, NI_MAXSERV,
881
GError *e = gibber_resolver_gai_error_to_g_error (ret);
883
gibber_resolver_nameinfo_result (resolver, id, NULL, NULL, e);
888
gibber_resolver_nameinfo_result (resolver, id, g_strdup (name),
889
g_strdup (servicename), NULL);
895
resolver_resolv_cancel (GibberResolver *resolver, guint id)