Fix compilation on Android with the bionic C library
[platform/upstream/glib.git] / gio / gthreadedresolver.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 <stdio.h>
28 #include <string.h>
29
30 #include "gthreadedresolver.h"
31 #include "gnetworkingprivate.h"
32
33 #include "gcancellable.h"
34 #include "gtask.h"
35 #include "gsocketaddress.h"
36
37
38 G_DEFINE_TYPE (GThreadedResolver, g_threaded_resolver, G_TYPE_RESOLVER)
39
40 static void
41 g_threaded_resolver_init (GThreadedResolver *gtr)
42 {
43 }
44
45 static void
46 do_lookup_by_name (GTask         *task,
47                    gpointer       source_object,
48                    gpointer       task_data,
49                    GCancellable  *cancellable)
50 {
51   const char *hostname = task_data;
52   struct addrinfo *res = NULL;
53   GList *addresses;
54   gint retval;
55   GError *error = NULL;
56
57   retval = getaddrinfo (hostname, NULL, &_g_resolver_addrinfo_hints, &res);
58   addresses = _g_resolver_addresses_from_addrinfo (hostname, res, retval, &error);
59   if (res)
60     freeaddrinfo (res);
61
62   if (addresses)
63     {
64       g_task_return_pointer (task, addresses,
65                              (GDestroyNotify)g_resolver_free_addresses);
66     }
67   else
68     g_task_return_error (task, error);
69 }
70
71 static GList *
72 lookup_by_name (GResolver     *resolver,
73                 const gchar   *hostname,
74                 GCancellable  *cancellable,
75                 GError       **error)
76 {
77   GTask *task;
78   GList *addresses;
79
80   task = g_task_new (resolver, cancellable, NULL, NULL);
81   g_task_set_task_data (task, g_strdup (hostname), g_free);
82   g_task_set_return_on_cancel (task, TRUE);
83   g_task_run_in_thread_sync (task, do_lookup_by_name);
84   addresses = g_task_propagate_pointer (task, error);
85   g_object_unref (task);
86
87   return addresses;
88 }
89
90 static void
91 lookup_by_name_async (GResolver           *resolver,
92                       const gchar         *hostname,
93                       GCancellable        *cancellable,
94                       GAsyncReadyCallback  callback,
95                       gpointer             user_data)
96 {
97   GTask *task;
98
99   task = g_task_new (resolver, cancellable, callback, user_data);
100   g_task_set_task_data (task, g_strdup (hostname), g_free);
101   g_task_set_return_on_cancel (task, TRUE);
102   g_task_run_in_thread (task, do_lookup_by_name);
103   g_object_unref (task);
104 }
105
106 static GList *
107 lookup_by_name_finish (GResolver     *resolver,
108                        GAsyncResult  *result,
109                        GError       **error)
110 {
111   g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
112
113   return g_task_propagate_pointer (G_TASK (result), error);
114 }
115
116
117 static void
118 do_lookup_by_address (GTask         *task,
119                       gpointer       source_object,
120                       gpointer       task_data,
121                       GCancellable  *cancellable)
122 {
123   GInetAddress *address = task_data;
124   struct sockaddr_storage sockaddr;
125   gsize sockaddr_size;
126   gchar namebuf[NI_MAXHOST], *name;
127   gint retval;
128   GError *error = NULL;
129
130   _g_resolver_address_to_sockaddr (address, &sockaddr, &sockaddr_size);
131   retval = getnameinfo ((struct sockaddr *)&sockaddr, sockaddr_size,
132                         namebuf, sizeof (namebuf), NULL, 0, NI_NAMEREQD);
133   name = _g_resolver_name_from_nameinfo (address, namebuf, retval, &error);
134
135   if (name)
136     g_task_return_pointer (task, name, g_free);
137   else
138     g_task_return_error (task, error);
139 }
140
141 static gchar *
142 lookup_by_address (GResolver        *resolver,
143                    GInetAddress     *address,
144                    GCancellable     *cancellable,
145                    GError          **error)
146 {
147   GTask *task;
148   gchar *name;
149
150   task = g_task_new (resolver, cancellable, NULL, NULL);
151   g_task_set_task_data (task, g_object_ref (address), g_object_unref);
152   g_task_set_return_on_cancel (task, TRUE);
153   g_task_run_in_thread_sync (task, do_lookup_by_address);
154   name = g_task_propagate_pointer (task, error);
155   g_object_unref (task);
156
157   return name;
158 }
159
160 static void
161 lookup_by_address_async (GResolver           *resolver,
162                          GInetAddress        *address,
163                          GCancellable        *cancellable,
164                          GAsyncReadyCallback  callback,
165                          gpointer             user_data)
166 {
167   GTask *task;
168
169   task = g_task_new (resolver, cancellable, callback, user_data);
170   g_task_set_task_data (task, g_object_ref (address), g_object_unref);
171   g_task_set_return_on_cancel (task, TRUE);
172   g_task_run_in_thread (task, do_lookup_by_address);
173   g_object_unref (task);
174 }
175
176 static gchar *
177 lookup_by_address_finish (GResolver     *resolver,
178                           GAsyncResult  *result,
179                           GError       **error)
180 {
181   g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
182
183   return g_task_propagate_pointer (G_TASK (result), error);
184 }
185
186
187 typedef struct {
188   char *rrname;
189   GResolverRecordType record_type;
190 } LookupRecordsData;
191
192 static void
193 free_lookup_records_data (LookupRecordsData *lrd)
194 {
195   g_free (lrd->rrname);
196   g_slice_free (LookupRecordsData, lrd);
197 }
198
199 static void
200 free_records (GList *records)
201 {
202   g_list_free_full (records, (GDestroyNotify) g_variant_unref);
203 }
204
205 #if defined(G_OS_UNIX)
206 #ifdef __BIONIC__
207 #define C_IN 1
208 int res_query(const char *, int, int, u_char *, int);
209 #endif
210 #endif
211
212 static void
213 do_lookup_records (GTask         *task,
214                    gpointer       source_object,
215                    gpointer       task_data,
216                    GCancellable  *cancellable)
217 {
218   LookupRecordsData *lrd = task_data;
219   GList *records;
220   GError *error = NULL;
221 #if defined(G_OS_UNIX)
222   gint len = 512;
223   gint herr;
224   GByteArray *answer;
225   gint rrtype;
226
227   rrtype = _g_resolver_record_type_to_rrtype (lrd->record_type);
228   answer = g_byte_array_new ();
229   for (;;)
230     {
231       g_byte_array_set_size (answer, len * 2);
232       len = res_query (lrd->rrname, C_IN, rrtype, answer->data, answer->len);
233
234       /* If answer fit in the buffer then we're done */
235       if (len < 0 || len < (gint)answer->len)
236         break;
237
238       /*
239        * On overflow some res_query's return the length needed, others
240        * return the full length entered. This code works in either case.
241        */
242   }
243
244   herr = h_errno;
245   records = _g_resolver_records_from_res_query (lrd->rrname, rrtype, answer->data, len, herr, &error);
246   g_byte_array_free (answer, TRUE);
247
248 #elif defined(G_OS_WIN32)
249   DNS_STATUS status;
250   DNS_RECORD *results = NULL;
251   WORD dnstype;
252
253   dnstype = _g_resolver_record_type_to_dnstype (lrd->record_type);
254   status = DnsQuery_A (lrd->rrname, dnstype, DNS_QUERY_STANDARD, NULL, &results, NULL);
255   records = _g_resolver_records_from_DnsQuery (lrd->rrname, dnstype, status, results, &error);
256   if (results != NULL)
257     DnsRecordListFree (results, DnsFreeRecordList);
258 #endif
259
260   if (records)
261     {
262       g_task_return_pointer (task, records, (GDestroyNotify) free_records);
263     }
264   else
265     g_task_return_error (task, error);
266 }
267
268 static GList *
269 lookup_records (GResolver              *resolver,
270                 const gchar            *rrname,
271                 GResolverRecordType     record_type,
272                 GCancellable           *cancellable,
273                 GError                **error)
274 {
275   GTask *task;
276   GList *records;
277   LookupRecordsData *lrd;
278
279   task = g_task_new (resolver, cancellable, NULL, NULL);
280
281   lrd = g_slice_new (LookupRecordsData);
282   lrd->rrname = g_strdup (rrname);
283   lrd->record_type = record_type;
284   g_task_set_task_data (task, lrd, (GDestroyNotify) free_lookup_records_data);
285
286   g_task_set_return_on_cancel (task, TRUE);
287   g_task_run_in_thread_sync (task, do_lookup_records);
288   records = g_task_propagate_pointer (task, error);
289   g_object_unref (task);
290
291   return records;
292 }
293
294 static void
295 lookup_records_async (GResolver           *resolver,
296                       const char          *rrname,
297                       GResolverRecordType  record_type,
298                       GCancellable        *cancellable,
299                       GAsyncReadyCallback  callback,
300                       gpointer             user_data)
301 {
302   GTask *task;
303   LookupRecordsData *lrd;
304
305   task = g_task_new (resolver, cancellable, callback, user_data);
306
307   lrd = g_slice_new (LookupRecordsData);
308   lrd->rrname = g_strdup (rrname);
309   lrd->record_type = record_type;
310   g_task_set_task_data (task, lrd, (GDestroyNotify) free_lookup_records_data);
311
312   g_task_set_return_on_cancel (task, TRUE);
313   g_task_run_in_thread (task, do_lookup_records);
314   g_object_unref (task);
315 }
316
317 static GList *
318 lookup_records_finish (GResolver     *resolver,
319                        GAsyncResult  *result,
320                        GError       **error)
321 {
322   g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
323
324   return g_task_propagate_pointer (G_TASK (result), error);
325 }
326
327
328 static void
329 g_threaded_resolver_class_init (GThreadedResolverClass *threaded_class)
330 {
331   GResolverClass *resolver_class = G_RESOLVER_CLASS (threaded_class);
332
333   resolver_class->lookup_by_name           = lookup_by_name;
334   resolver_class->lookup_by_name_async     = lookup_by_name_async;
335   resolver_class->lookup_by_name_finish    = lookup_by_name_finish;
336   resolver_class->lookup_by_address        = lookup_by_address;
337   resolver_class->lookup_by_address_async  = lookup_by_address_async;
338   resolver_class->lookup_by_address_finish = lookup_by_address_finish;
339   resolver_class->lookup_records           = lookup_records;
340   resolver_class->lookup_records_async     = lookup_records_async;
341   resolver_class->lookup_records_finish    = lookup_records_finish;
342 }