gio: move resolver utils from gresolver.c to gthreadedresolver.c
[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 "ginetaddress.h"
35 #include "ginetsocketaddress.h"
36 #include "gtask.h"
37 #include "gsocketaddress.h"
38 #include "gsrvtarget.h"
39
40
41 G_DEFINE_TYPE (GThreadedResolver, g_threaded_resolver, G_TYPE_RESOLVER)
42
43 static void
44 g_threaded_resolver_init (GThreadedResolver *gtr)
45 {
46 }
47
48 static GResolverError
49 g_resolver_error_from_addrinfo_error (gint err)
50 {
51   switch (err)
52     {
53     case EAI_FAIL:
54 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
55     case EAI_NODATA:
56 #endif
57     case EAI_NONAME:
58       return G_RESOLVER_ERROR_NOT_FOUND;
59
60     case EAI_AGAIN:
61       return G_RESOLVER_ERROR_TEMPORARY_FAILURE;
62
63     default:
64       return G_RESOLVER_ERROR_INTERNAL;
65     }
66 }
67
68 static struct addrinfo addrinfo_hints;
69
70 static void
71 do_lookup_by_name (GTask         *task,
72                    gpointer       source_object,
73                    gpointer       task_data,
74                    GCancellable  *cancellable)
75 {
76   const char *hostname = task_data;
77   struct addrinfo *res = NULL;
78   GList *addresses;
79   gint retval;
80
81   retval = getaddrinfo (hostname, NULL, &addrinfo_hints, &res);
82
83   if (retval == 0)
84     {
85       struct addrinfo *ai;
86       GSocketAddress *sockaddr;
87       GInetAddress *addr;
88
89       addresses = NULL;
90       for (ai = res; ai; ai = ai->ai_next)
91         {
92           sockaddr = g_socket_address_new_from_native (ai->ai_addr, ai->ai_addrlen);
93           if (!sockaddr || !G_IS_INET_SOCKET_ADDRESS (sockaddr))
94             continue;
95
96           addr = g_object_ref (g_inet_socket_address_get_address ((GInetSocketAddress *)sockaddr));
97           addresses = g_list_prepend (addresses, addr);
98           g_object_unref (sockaddr);
99         }
100
101       addresses = g_list_reverse (addresses);
102       g_task_return_pointer (task, addresses,
103                              (GDestroyNotify)g_resolver_free_addresses);
104     }
105   else
106     {
107       g_task_return_new_error (task,
108                                G_RESOLVER_ERROR,
109                                g_resolver_error_from_addrinfo_error (retval),
110                                _("Error resolving '%s': %s"),
111                                hostname, gai_strerror (retval));
112     }
113
114   if (res)
115     freeaddrinfo (res);
116 }
117
118 static GList *
119 lookup_by_name (GResolver     *resolver,
120                 const gchar   *hostname,
121                 GCancellable  *cancellable,
122                 GError       **error)
123 {
124   GTask *task;
125   GList *addresses;
126
127   task = g_task_new (resolver, cancellable, NULL, NULL);
128   g_task_set_task_data (task, g_strdup (hostname), g_free);
129   g_task_set_return_on_cancel (task, TRUE);
130   g_task_run_in_thread_sync (task, do_lookup_by_name);
131   addresses = g_task_propagate_pointer (task, error);
132   g_object_unref (task);
133
134   return addresses;
135 }
136
137 static void
138 lookup_by_name_async (GResolver           *resolver,
139                       const gchar         *hostname,
140                       GCancellable        *cancellable,
141                       GAsyncReadyCallback  callback,
142                       gpointer             user_data)
143 {
144   GTask *task;
145
146   task = g_task_new (resolver, cancellable, callback, user_data);
147   g_task_set_task_data (task, g_strdup (hostname), g_free);
148   g_task_set_return_on_cancel (task, TRUE);
149   g_task_run_in_thread (task, do_lookup_by_name);
150   g_object_unref (task);
151 }
152
153 static GList *
154 lookup_by_name_finish (GResolver     *resolver,
155                        GAsyncResult  *result,
156                        GError       **error)
157 {
158   g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
159
160   return g_task_propagate_pointer (G_TASK (result), error);
161 }
162
163
164 static void
165 do_lookup_by_address (GTask         *task,
166                       gpointer       source_object,
167                       gpointer       task_data,
168                       GCancellable  *cancellable)
169 {
170   GInetAddress *address = task_data;
171   struct sockaddr_storage sockaddr;
172   gsize sockaddr_size;
173   GSocketAddress *gsockaddr;
174   gchar name[NI_MAXHOST];
175   gint retval;
176
177   gsockaddr = g_inet_socket_address_new (address, 0);
178   g_socket_address_to_native (gsockaddr, (struct sockaddr *)&sockaddr,
179                               sizeof (sockaddr), NULL);
180   sockaddr_size = g_socket_address_get_native_size (gsockaddr);
181   g_object_unref (gsockaddr);
182
183   retval = getnameinfo ((struct sockaddr *)&sockaddr, sockaddr_size,
184                         name, sizeof (name), NULL, 0, NI_NAMEREQD);
185   if (retval == 0)
186     g_task_return_pointer (task, g_strdup (name), g_free);
187   else
188     {
189       gchar *phys;
190
191       phys = g_inet_address_to_string (address);
192       g_task_return_new_error (task,
193                                G_RESOLVER_ERROR,
194                                g_resolver_error_from_addrinfo_error (retval),
195                                _("Error reverse-resolving '%s': %s"),
196                                phys ? phys : "(unknown)",
197                                gai_strerror (retval));
198       g_free (phys);
199     }
200 }
201
202 static gchar *
203 lookup_by_address (GResolver        *resolver,
204                    GInetAddress     *address,
205                    GCancellable     *cancellable,
206                    GError          **error)
207 {
208   GTask *task;
209   gchar *name;
210
211   task = g_task_new (resolver, cancellable, NULL, NULL);
212   g_task_set_task_data (task, g_object_ref (address), g_object_unref);
213   g_task_set_return_on_cancel (task, TRUE);
214   g_task_run_in_thread_sync (task, do_lookup_by_address);
215   name = g_task_propagate_pointer (task, error);
216   g_object_unref (task);
217
218   return name;
219 }
220
221 static void
222 lookup_by_address_async (GResolver           *resolver,
223                          GInetAddress        *address,
224                          GCancellable        *cancellable,
225                          GAsyncReadyCallback  callback,
226                          gpointer             user_data)
227 {
228   GTask *task;
229
230   task = g_task_new (resolver, cancellable, callback, user_data);
231   g_task_set_task_data (task, g_object_ref (address), g_object_unref);
232   g_task_set_return_on_cancel (task, TRUE);
233   g_task_run_in_thread (task, do_lookup_by_address);
234   g_object_unref (task);
235 }
236
237 static gchar *
238 lookup_by_address_finish (GResolver     *resolver,
239                           GAsyncResult  *result,
240                           GError       **error)
241 {
242   g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
243
244   return g_task_propagate_pointer (G_TASK (result), error);
245 }
246
247
248 #if defined(G_OS_UNIX)
249 static GVariant *
250 parse_res_srv (guchar  *answer,
251                guchar  *end,
252                guchar **p)
253 {
254   gchar namebuf[1024];
255   guint16 priority, weight, port;
256
257   GETSHORT (priority, *p);
258   GETSHORT (weight, *p);
259   GETSHORT (port, *p);
260   *p += dn_expand (answer, end, *p, namebuf, sizeof (namebuf));
261
262   return g_variant_new ("(qqqs)",
263                         priority,
264                         weight,
265                         port,
266                         namebuf);
267 }
268
269 static GVariant *
270 parse_res_soa (guchar  *answer,
271                guchar  *end,
272                guchar **p)
273 {
274   gchar mnamebuf[1024];
275   gchar rnamebuf[1024];
276   guint32 serial, refresh, retry, expire, ttl;
277
278   *p += dn_expand (answer, end, *p, mnamebuf, sizeof (mnamebuf));
279   *p += dn_expand (answer, end, *p, rnamebuf, sizeof (rnamebuf));
280
281   GETLONG (serial, *p);
282   GETLONG (refresh, *p);
283   GETLONG (retry, *p);
284   GETLONG (expire, *p);
285   GETLONG (ttl, *p);
286
287   return g_variant_new ("(ssuuuuu)",
288                         mnamebuf,
289                         rnamebuf,
290                         serial,
291                         refresh,
292                         retry,
293                         expire,
294                         ttl);
295 }
296
297 static GVariant *
298 parse_res_ns (guchar  *answer,
299               guchar  *end,
300               guchar **p)
301 {
302   gchar namebuf[1024];
303
304   *p += dn_expand (answer, end, *p, namebuf, sizeof (namebuf));
305
306   return g_variant_new ("(s)", namebuf);
307 }
308
309 static GVariant *
310 parse_res_mx (guchar  *answer,
311               guchar  *end,
312               guchar **p)
313 {
314   gchar namebuf[1024];
315   guint16 preference;
316
317   GETSHORT (preference, *p);
318
319   *p += dn_expand (answer, end, *p, namebuf, sizeof (namebuf));
320
321   return g_variant_new ("(qs)",
322                         preference,
323                         namebuf);
324 }
325
326 static GVariant *
327 parse_res_txt (guchar  *answer,
328                guchar  *end,
329                guchar **p)
330 {
331   GVariant *record;
332   GPtrArray *array;
333   guchar *at = *p;
334   gsize len;
335
336   array = g_ptr_array_new_with_free_func (g_free);
337   while (at < end)
338     {
339       len = *(at++);
340       if (len > at - end)
341         break;
342       g_ptr_array_add (array, g_strndup ((gchar *)at, len));
343       at += len;
344     }
345
346   *p = at;
347   record = g_variant_new ("(@as)",
348                           g_variant_new_strv ((const gchar **)array->pdata, array->len));
349   g_ptr_array_free (array, TRUE);
350   return record;
351 }
352
353 static gint
354 g_resolver_record_type_to_rrtype (GResolverRecordType type)
355 {
356   switch (type)
357   {
358     case G_RESOLVER_RECORD_SRV:
359       return T_SRV;
360     case G_RESOLVER_RECORD_TXT:
361       return T_TXT;
362     case G_RESOLVER_RECORD_SOA:
363       return T_SOA;
364     case G_RESOLVER_RECORD_NS:
365       return T_NS;
366     case G_RESOLVER_RECORD_MX:
367       return T_MX;
368   }
369   g_return_val_if_reached (-1);
370 }
371
372 static GList *
373 g_resolver_records_from_res_query (const gchar      *rrname,
374                                    gint              rrtype,
375                                    guchar           *answer,
376                                    gint              len,
377                                    gint              herr,
378                                    GError          **error)
379 {
380   gint count;
381   gchar namebuf[1024];
382   guchar *end, *p;
383   guint16 type, qclass, rdlength;
384   guint32 ttl;
385   HEADER *header;
386   GList *records;
387   GVariant *record;
388
389   if (len <= 0)
390     {
391       GResolverError errnum;
392       const gchar *format;
393
394       if (len == 0 || herr == HOST_NOT_FOUND || herr == NO_DATA)
395         {
396           errnum = G_RESOLVER_ERROR_NOT_FOUND;
397           format = _("No DNS record of the requested type for '%s'");
398         }
399       else if (herr == TRY_AGAIN)
400         {
401           errnum = G_RESOLVER_ERROR_TEMPORARY_FAILURE;
402           format = _("Temporarily unable to resolve '%s'");
403         }
404       else
405         {
406           errnum = G_RESOLVER_ERROR_INTERNAL;
407           format = _("Error resolving '%s'");
408         }
409
410       g_set_error (error, G_RESOLVER_ERROR, errnum, format, rrname);
411       return NULL;
412     }
413
414   records = NULL;
415
416   header = (HEADER *)answer;
417   p = answer + sizeof (HEADER);
418   end = answer + len;
419
420   /* Skip query */
421   count = ntohs (header->qdcount);
422   while (count-- && p < end)
423     {
424       p += dn_expand (answer, end, p, namebuf, sizeof (namebuf));
425       p += 4;
426
427       /* To silence gcc warnings */
428       namebuf[0] = namebuf[1];
429     }
430
431   /* Read answers */
432   count = ntohs (header->ancount);
433   while (count-- && p < end)
434     {
435       p += dn_expand (answer, end, p, namebuf, sizeof (namebuf));
436       GETSHORT (type, p);
437       GETSHORT (qclass, p);
438       GETLONG  (ttl, p);
439       ttl = ttl; /* To avoid -Wunused-but-set-variable */
440       GETSHORT (rdlength, p);
441
442       if (type != rrtype || qclass != C_IN)
443         {
444           p += rdlength;
445           continue;
446         }
447
448       switch (rrtype)
449         {
450         case T_SRV:
451           record = parse_res_srv (answer, end, &p);
452           break;
453         case T_MX:
454           record = parse_res_mx (answer, end, &p);
455           break;
456         case T_SOA:
457           record = parse_res_soa (answer, end, &p);
458           break;
459         case T_NS:
460           record = parse_res_ns (answer, end, &p);
461           break;
462         case T_TXT:
463           record = parse_res_txt (answer, p + rdlength, &p);
464           break;
465         default:
466           g_warn_if_reached ();
467           record = NULL;
468           break;
469         }
470
471       if (record != NULL)
472         records = g_list_prepend (records, record);
473     }
474
475     return records;
476 }
477
478 #elif defined(G_OS_WIN32)
479
480 static GVariant *
481 parse_dns_srv (DNS_RECORD *rec)
482 {
483   return g_variant_new ("(qqqs)",
484                         (guint16)rec->Data.SRV.wPriority,
485                         (guint16)rec->Data.SRV.wWeight,
486                         (guint16)rec->Data.SRV.wPort,
487                         rec->Data.SRV.pNameTarget);
488 }
489
490 static GVariant *
491 parse_dns_soa (DNS_RECORD *rec)
492 {
493   return g_variant_new ("(ssuuuuu)",
494                         rec->Data.SOA.pNamePrimaryServer,
495                         rec->Data.SOA.pNameAdministrator,
496                         (guint32)rec->Data.SOA.dwSerialNo,
497                         (guint32)rec->Data.SOA.dwRefresh,
498                         (guint32)rec->Data.SOA.dwRetry,
499                         (guint32)rec->Data.SOA.dwExpire,
500                         (guint32)rec->Data.SOA.dwDefaultTtl);
501 }
502
503 static GVariant *
504 parse_dns_ns (DNS_RECORD *rec)
505 {
506   return g_variant_new ("(s)", rec->Data.NS.pNameHost);
507 }
508
509 static GVariant *
510 parse_dns_mx (DNS_RECORD *rec)
511 {
512   return g_variant_new ("(qs)",
513                         (guint16)rec->Data.MX.wPreference,
514                         rec->Data.MX.pNameExchange);
515 }
516
517 static GVariant *
518 parse_dns_txt (DNS_RECORD *rec)
519 {
520   GVariant *record;
521   GPtrArray *array;
522   DWORD i;
523
524   array = g_ptr_array_new ();
525   for (i = 0; i < rec->Data.TXT.dwStringCount; i++)
526     g_ptr_array_add (array, rec->Data.TXT.pStringArray[i]);
527   record = g_variant_new ("(@as)",
528                           g_variant_new_strv ((const gchar **)array->pdata, array->len));
529   g_ptr_array_free (array, TRUE);
530   return record;
531 }
532
533 static WORD
534 g_resolver_record_type_to_dnstype (GResolverRecordType type)
535 {
536   switch (type)
537   {
538     case G_RESOLVER_RECORD_SRV:
539       return DNS_TYPE_SRV;
540     case G_RESOLVER_RECORD_TXT:
541       return DNS_TYPE_TEXT;
542     case G_RESOLVER_RECORD_SOA:
543       return DNS_TYPE_SOA;
544     case G_RESOLVER_RECORD_NS:
545       return DNS_TYPE_NS;
546     case G_RESOLVER_RECORD_MX:
547       return DNS_TYPE_MX;
548   }
549   g_return_val_if_reached (-1);
550 }
551
552 static GList *
553 g_resolver_records_from_DnsQuery (const gchar  *rrname,
554                                   WORD          dnstype,
555                                   DNS_STATUS    status,
556                                   DNS_RECORD   *results,
557                                   GError      **error)
558 {
559   DNS_RECORD *rec;
560   gpointer record;
561   GList *records;
562
563   if (status != ERROR_SUCCESS)
564     {
565       GResolverError errnum;
566       const gchar *format;
567
568       if (status == DNS_ERROR_RCODE_NAME_ERROR)
569         {
570           errnum = G_RESOLVER_ERROR_NOT_FOUND;
571           format = _("No DNS record of the requested type for '%s'");
572         }
573       else if (status == DNS_ERROR_RCODE_SERVER_FAILURE)
574         {
575           errnum = G_RESOLVER_ERROR_TEMPORARY_FAILURE;
576           format = _("Temporarily unable to resolve '%s'");
577         }
578       else
579         {
580           errnum = G_RESOLVER_ERROR_INTERNAL;
581           format = _("Error resolving '%s'");
582         }
583
584       g_set_error (error, G_RESOLVER_ERROR, errnum, format, rrname);
585       return NULL;
586     }
587
588   records = NULL;
589   for (rec = results; rec; rec = rec->pNext)
590     {
591       if (rec->wType != dnstype)
592         continue;
593       switch (dnstype)
594         {
595         case DNS_TYPE_SRV:
596           record = parse_dns_srv (rec);
597           break;
598         case DNS_TYPE_SOA:
599           record = parse_dns_soa (rec);
600           break;
601         case DNS_TYPE_NS:
602           record = parse_dns_ns (rec);
603           break;
604         case DNS_TYPE_MX:
605           record = parse_dns_mx (rec);
606           break;
607         case DNS_TYPE_TEXT:
608           record = parse_dns_txt (rec);
609           break;
610         default:
611           g_warn_if_reached ();
612           record = NULL;
613           break;
614         }
615       if (record != NULL)
616         records = g_list_prepend (records, g_variant_ref_sink (record));
617     }
618
619   return records;
620 }
621
622 #endif
623
624 typedef struct {
625   char *rrname;
626   GResolverRecordType record_type;
627 } LookupRecordsData;
628
629 static void
630 free_lookup_records_data (LookupRecordsData *lrd)
631 {
632   g_free (lrd->rrname);
633   g_slice_free (LookupRecordsData, lrd);
634 }
635
636 static void
637 free_records (GList *records)
638 {
639   g_list_free_full (records, (GDestroyNotify) g_variant_unref);
640 }
641
642 static void
643 do_lookup_records (GTask         *task,
644                    gpointer       source_object,
645                    gpointer       task_data,
646                    GCancellable  *cancellable)
647 {
648   LookupRecordsData *lrd = task_data;
649   GList *records;
650   GError *error = NULL;
651
652 #if defined(G_OS_UNIX)
653   gint len = 512;
654   gint herr;
655   GByteArray *answer;
656   gint rrtype;
657
658   rrtype = g_resolver_record_type_to_rrtype (lrd->record_type);
659   answer = g_byte_array_new ();
660   for (;;)
661     {
662       g_byte_array_set_size (answer, len * 2);
663       len = res_query (lrd->rrname, C_IN, rrtype, answer->data, answer->len);
664
665       /* If answer fit in the buffer then we're done */
666       if (len < 0 || len < (gint)answer->len)
667         break;
668
669       /*
670        * On overflow some res_query's return the length needed, others
671        * return the full length entered. This code works in either case.
672        */
673     }
674
675   herr = h_errno;
676   records = g_resolver_records_from_res_query (lrd->rrname, rrtype, answer->data, len, herr, &error);
677   g_byte_array_free (answer, TRUE);
678
679 #else
680
681   DNS_STATUS status;
682   DNS_RECORD *results = NULL;
683   WORD dnstype;
684
685   dnstype = g_resolver_record_type_to_dnstype (lrd->record_type);
686   status = DnsQuery_A (lrd->rrname, dnstype, DNS_QUERY_STANDARD, NULL, &results, NULL);
687   records = g_resolver_records_from_DnsQuery (lrd->rrname, dnstype, status, results, &error);
688   if (results != NULL)
689     DnsRecordListFree (results, DnsFreeRecordList);
690
691 #endif
692
693   if (records)
694     g_task_return_pointer (task, records, (GDestroyNotify) free_records);
695   else
696     g_task_return_error (task, error);
697 }
698
699 static GList *
700 lookup_records (GResolver              *resolver,
701                 const gchar            *rrname,
702                 GResolverRecordType     record_type,
703                 GCancellable           *cancellable,
704                 GError                **error)
705 {
706   GTask *task;
707   GList *records;
708   LookupRecordsData *lrd;
709
710   task = g_task_new (resolver, cancellable, NULL, NULL);
711
712   lrd = g_slice_new (LookupRecordsData);
713   lrd->rrname = g_strdup (rrname);
714   lrd->record_type = record_type;
715   g_task_set_task_data (task, lrd, (GDestroyNotify) free_lookup_records_data);
716
717   g_task_set_return_on_cancel (task, TRUE);
718   g_task_run_in_thread_sync (task, do_lookup_records);
719   records = g_task_propagate_pointer (task, error);
720   g_object_unref (task);
721
722   return records;
723 }
724
725 static void
726 lookup_records_async (GResolver           *resolver,
727                       const char          *rrname,
728                       GResolverRecordType  record_type,
729                       GCancellable        *cancellable,
730                       GAsyncReadyCallback  callback,
731                       gpointer             user_data)
732 {
733   GTask *task;
734   LookupRecordsData *lrd;
735
736   task = g_task_new (resolver, cancellable, callback, user_data);
737
738   lrd = g_slice_new (LookupRecordsData);
739   lrd->rrname = g_strdup (rrname);
740   lrd->record_type = record_type;
741   g_task_set_task_data (task, lrd, (GDestroyNotify) free_lookup_records_data);
742
743   g_task_set_return_on_cancel (task, TRUE);
744   g_task_run_in_thread (task, do_lookup_records);
745   g_object_unref (task);
746 }
747
748 static GList *
749 lookup_records_finish (GResolver     *resolver,
750                        GAsyncResult  *result,
751                        GError       **error)
752 {
753   g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
754
755   return g_task_propagate_pointer (G_TASK (result), error);
756 }
757
758
759 static void
760 g_threaded_resolver_class_init (GThreadedResolverClass *threaded_class)
761 {
762   GResolverClass *resolver_class = G_RESOLVER_CLASS (threaded_class);
763
764   resolver_class->lookup_by_name           = lookup_by_name;
765   resolver_class->lookup_by_name_async     = lookup_by_name_async;
766   resolver_class->lookup_by_name_finish    = lookup_by_name_finish;
767   resolver_class->lookup_by_address        = lookup_by_address;
768   resolver_class->lookup_by_address_async  = lookup_by_address_async;
769   resolver_class->lookup_by_address_finish = lookup_by_address_finish;
770   resolver_class->lookup_records           = lookup_records;
771   resolver_class->lookup_records_async     = lookup_records_async;
772   resolver_class->lookup_records_finish    = lookup_records_finish;
773
774   /* Initialize _g_resolver_addrinfo_hints */
775 #ifdef AI_ADDRCONFIG
776   addrinfo_hints.ai_flags |= AI_ADDRCONFIG;
777 #endif
778   /* These two don't actually matter, they just get copied into the
779    * returned addrinfo structures (and then we ignore them). But if
780    * we leave them unset, we'll get back duplicate answers.
781    */
782   addrinfo_hints.ai_socktype = SOCK_STREAM;
783   addrinfo_hints.ai_protocol = IPPROTO_TCP;
784 }