fix crash with ipv6 nameserver usage on mac
[psi:iris.git] / src / jdns / jdns_sys.c
1 /*
2  * Copyright (C) 2005-2008  Justin Karneges
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23
24 /*
25 this code probes the system for dns settings.  blah.
26
27 q3dns strategies
28 ----------------
29
30 windows:
31
32 domain name, name server, "search list" found in windows registry here:
33
34   HKEY_LOCAL_MACHINE
35   System\CurrentControlSet\Services\Tcpip\Parameters  <-- win nt+
36   System\CurrentControlSet\Services\VxD\MSTCP  <-- win 98
37
38   for domain, try DhcpDomain else Domain
39   for name servers, try DhcpNameServer, else NameServer
40   for search list, try SearchList
41
42 iphlpapi.dll : GetNetworkParams(PFIXED_INFO, PULONG);
43
44   info->DomainName
45   info->DnsServerList (if not null, use it, and loop through ->Next until
46     null)
47   no search list
48
49 first try getnetworkparams.  if that fails, try the nt regkey then the 98
50   regkey.  it seems that search list can only come from the registry, so
51   maybe we need to grab that entry even if getnetworkparams works.
52
53 in the case of the registry, the nameserver and searchlist entries are
54   delimited by spaces on win nt and commas on win 98.  probably a good
55   idea to simplify white space first (chop away space at start and end,
56   reduce all sections of spaces to one char).  also, lowercase the search
57   list.
58
59 qt doesn't read the hosts file on windows.  this might be a good idea, but
60   probably not worth it.
61
62 unix:
63
64 read /etc/resolv.conf manually:
65   for each line, split at spaces
66   if the first item is "nameserver", then there should be an IP address
67     following it.  note: may contain mixed ipv4 and ipv6 addresses
68   if the first item is "search", all other items are added to the domain
69     list
70   if the first item is "domain", then the next item should be added to the
71     domain list.
72   do case-insensitive matching for the item types
73   for search/domain, the items are in the 8-bit system locale
74
75 info can also be fetched using system calls.  we use the res_* stuff here.
76   first we should detect for a "modern res api".  this is available from
77   glibc 2.3 and onward.  use the following scheme to check for it:
78
79 #if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2)
80   && (__GLIBC_MINOR__ >= 3)))
81   // modern res api
82 #endif
83
84 on mac we should look up res_init in the system library.  see:
85   qt_mac_resolve_sys(RTLD_NEXT, "res_init"); for a hint.
86 otherwise we can just use res_init() straight.
87
88 under a modern res api, we do:
89   struct __res_state res;
90   res_ninit(&res);
91 otherwise, we simply call res_init().  for the modern api, we use the "res"
92   struct that we made.  otherwise, we use the global "_res" struct.
93
94 read the res struct to obtain the name servers, search list, and domain.
95   lowercase the search list and domain.
96
97 qt tries the file, and if that fails it tries the syscalls.  we may want to
98   do the syscalls first, or even just do both all the time.
99
100 read /etc/hosts manually:
101   for each line
102     if there is a '#' character in the line, remove it and everything to
103       the right
104     simplify white space
105     convert to lowercase
106   split the line at spaces
107   first item is the ip address
108   all remaining items are hostnames
109
110   note: these hosts could also be used for reverse-dns too
111   note2: Windows has a hosts file as well (like C:\WINDOWS\hosts)
112 */
113
114 #include "jdns_p.h"
115
116 #ifdef JDNS_OS_WIN
117 # include <windows.h>
118 #endif
119
120 #ifdef JDNS_OS_UNIX
121 # include <netinet/in.h>
122 # include <arpa/nameser.h>
123 # include <resolv.h>
124 # include <dlfcn.h>
125 #endif
126
127 #define string_indexOf jdns_string_indexOf
128 #define string_split jdns_string_split
129
130 static int char_isspace(unsigned char c)
131 {
132         if(c == ' ' || c == '\t' || c == '\n' || c == '\r')
133                 return 1;
134         return 0;
135 }
136
137 static unsigned char *string_getnextword(unsigned char *in, int size, int pos, int *newpos)
138 {
139         int n;
140         int at;
141         int len;
142         unsigned char *out;
143
144         at = pos;
145
146         // skip any space at the start
147         while(at < size && char_isspace(in[at]))
148                 ++at;
149
150         // all space?  no word then
151         if(at >= size)
152                 return 0;
153
154         // skip until a space or end
155         n = at;
156         while(n < size && !char_isspace(in[n]))
157                 ++n;
158         len = n - at;
159
160         // allocate length + zero byte
161         out = (unsigned char *)jdns_alloc(len + 1);
162         if(!out)
163                 return 0;
164         memcpy(out, in + at, len);
165         out[len] = 0;
166         *newpos = at + len;
167         return out;
168 }
169
170 static jdns_string_t *string_simplify(const jdns_string_t *in)
171 {
172         int n;
173         int pos;
174         int total;
175         unsigned char *out;
176         int outlen;
177         jdns_string_t *outstr;
178         jdns_stringlist_t *wordlist;
179
180         // gather words and total of lengths
181         pos = 0;
182         total = 0;
183         wordlist = jdns_stringlist_new();
184         while(1)
185         {
186                 jdns_string_t *word;
187                 unsigned char *str = string_getnextword(in->data, in->size, pos, &pos);
188                 if(!str)
189                         break;
190                 word = jdns_string_new();
191                 jdns_string_set_cstr(word, (char *)str);
192                 jdns_free(str);
193                 jdns_stringlist_append(wordlist, word);
194                 total += word->size;
195                 jdns_string_delete(word);
196         }
197
198         if(total == 0)
199         {
200                 jdns_stringlist_delete(wordlist);
201
202                 outstr = jdns_string_new();
203                 jdns_string_set_cstr(outstr, "");
204                 return outstr;
205         }
206
207         // we need to allocate space for total lengths and wordcount-1 spaces
208         outlen = total + (wordlist->count - 1);
209         out = (unsigned char *)jdns_alloc(outlen);
210
211         // lay out the words
212         pos = 0;
213         for(n = 0; n < wordlist->count; ++n)
214         {
215                 unsigned char *data = wordlist->item[n]->data;
216                 int size = wordlist->item[n]->size;
217                 memcpy(out + pos, data, size);
218                 pos += size;
219
220                 // if this is not the last word, append a space
221                 if(n + 1 < wordlist->count)
222                         out[pos++] = ' ';
223         }
224         jdns_stringlist_delete(wordlist);
225
226         outstr = jdns_string_new();
227         jdns_string_set(outstr, out, outlen);
228         jdns_free(out);
229         return outstr;
230 }
231
232 static jdns_string_t *string_tolower(const jdns_string_t *in)
233 {
234         int n;
235         jdns_string_t *out = jdns_string_copy(in);
236         for(n = 0; n < out->size; ++n)
237                 out->data[n] = tolower(out->data[n]);
238         return out;
239 }
240
241 static jdns_string_t *file_nextline(FILE *f)
242 {
243         int at, size;
244         unsigned char *buf;
245         jdns_string_t *str;
246
247         size = 1023;
248         buf = (unsigned char *)jdns_alloc(size);
249         at = 0;
250         while(1)
251         {
252                 unsigned char c = fgetc(f);
253                 if(feof(f))
254                 {
255                         if(at > 0)
256                         {
257                                 // if we read at least one char, take it as a
258                                 //   line
259                                 break;
260                         }
261                         else
262                         {
263                                 jdns_free(buf);
264                                 return 0;
265                         }
266                 }
267                 if(c == '\n')
268                         break;
269                 if(c == '\r')
270                         continue;
271                 if(at < 1023)
272                         buf[at++] = c;
273         }
274
275         str = jdns_string_new();
276         jdns_string_set(str, buf, at);
277         jdns_free(buf);
278         return str;
279 }
280
281 static jdns_dnshostlist_t *read_hosts_file(const char *path)
282 {
283         jdns_dnshostlist_t *out;
284         FILE *f;
285         jdns_string_t *line, *simp;
286         jdns_stringlist_t *parts;
287         jdns_address_t *addr;
288         int n;
289
290         out = jdns_dnshostlist_new();
291
292         f = jdns_fopen(path, "r");
293         if(!f)
294                 return out;
295         while(1)
296         {
297                 line = file_nextline(f);
298                 if(!line)
299                         break;
300
301                 // truncate at comment
302                 n = string_indexOf(line, '#', 0);
303                 if(n != -1)
304                 {
305                         line->size = n;
306                         line->data[n] = 0;
307                 }
308
309                 simp = string_simplify(line);
310                 jdns_string_delete(line);
311
312                 parts = string_split(simp, ' ');
313                 jdns_string_delete(simp);
314
315                 if(parts->count < 2)
316                 {
317                         jdns_stringlist_delete(parts);
318                         continue;
319                 }
320
321                 addr = jdns_address_new();
322                 if(!jdns_address_set_cstr(addr, (const char *)parts->item[0]->data))
323                 {
324                         jdns_address_delete(addr);
325                         jdns_stringlist_delete(parts);
326                         continue;
327                 }
328
329                 for(n = 1; n < parts->count; ++n)
330                 {
331                         jdns_dnshost_t *h = jdns_dnshost_new();
332                         h->name = jdns_string_copy(parts->item[n]);
333                         h->address = jdns_address_copy(addr);
334                         jdns_dnshostlist_append(out, h);
335                         jdns_dnshost_delete(h);
336                 }
337
338                 jdns_address_delete(addr);
339                 jdns_stringlist_delete(parts);
340         }
341         fclose(f);
342         return out;
343 }
344
345 static void apply_hosts_file(jdns_dnsparams_t *a, const char *path)
346 {
347         int n;
348         jdns_dnshostlist_t *list;
349
350         list = read_hosts_file(path);
351         for(n = 0; n < list->count; ++n)
352                 jdns_dnshostlist_append(a->hosts, list->item[n]);
353         jdns_dnshostlist_delete(list);
354 }
355
356 static int dnsparams_have_domain(const jdns_dnsparams_t *a, const jdns_string_t *domain)
357 {
358         int n;
359         for(n = 0; n < a->domains->count; ++n)
360         {
361                 jdns_string_t *str = a->domains->item[n];
362                 if(strcmp((const char *)str->data, (const char *)domain->data) == 0)
363                         return 1;
364         }
365         return 0;
366 }
367
368 #ifdef JDNS_OS_WIN
369
370 // from Microsoft IPTypes.h
371 #ifndef IP_TYPES_INCLUDED
372 #define MAX_HOSTNAME_LEN    128
373 #define MAX_DOMAIN_NAME_LEN 128
374 #define MAX_SCOPE_ID_LEN    256
375 typedef struct {
376     char String[4 * 4];
377 } IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
378 typedef struct _IP_ADDR_STRING {
379     struct _IP_ADDR_STRING* Next;
380     IP_ADDRESS_STRING IpAddress;
381     IP_MASK_STRING IpMask;
382     DWORD Context;
383 } IP_ADDR_STRING, *PIP_ADDR_STRING;
384 typedef struct {
385     char HostName[MAX_HOSTNAME_LEN + 4] ;
386     char DomainName[MAX_DOMAIN_NAME_LEN + 4];
387     PIP_ADDR_STRING CurrentDnsServer;
388     IP_ADDR_STRING DnsServerList;
389     UINT NodeType;
390     char ScopeId[MAX_SCOPE_ID_LEN + 4];
391     UINT EnableRouting;
392     UINT EnableProxy;
393     UINT EnableDns;
394 } FIXED_INFO, *PFIXED_INFO;
395 #endif
396
397 typedef DWORD (WINAPI *GetNetworkParamsFunc)(PFIXED_INFO, PULONG);
398
399 static jdns_string_t *reg_readString(HKEY hk, const char *subkey)
400 {
401         char *buf;
402         DWORD bufsize;
403         int ret;
404         jdns_string_t *str = 0;
405
406         bufsize = 1024;
407         buf = (char *)jdns_alloc((int)bufsize);
408         if(!buf)
409                 return 0;
410         ret = RegQueryValueExA(hk, subkey, 0, 0, (LPBYTE)buf, &bufsize);
411         if(ret == ERROR_MORE_DATA)
412         {
413                 buf = (char *)jdns_realloc(buf, bufsize);
414                 if(!buf)
415                 {
416                         jdns_free(buf);
417                         return 0;
418                 }
419                 ret = RegQueryValueExA(hk, subkey, 0, 0, (LPBYTE)buf, &bufsize);
420         }
421         if(ret == ERROR_SUCCESS)
422         {
423                 str = jdns_string_new();
424                 jdns_string_set_cstr(str, (char *)buf);
425         }
426         jdns_free(buf);
427         return str;
428 }
429
430 static jdns_dnsparams_t *dnsparams_get_winreg()
431 {
432         int n;
433         jdns_dnsparams_t *params;
434         HKEY key;
435         int ret;
436         char sep;
437         jdns_string_t *str_domain, *str_nameserver, *str_searchlist;
438         jdns_stringlist_t *list_nameserver, *list_searchlist;
439
440         sep = ' ';
441         ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
442                 "System\\CurrentControlSet\\Services\\Tcpip\\Parameters",
443                 0, KEY_READ, &key);
444         if(ret != ERROR_SUCCESS)
445         {
446                 sep = ',';
447                 ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
448                         "System\\CurrentControlSet\\Services\\VxD\\MSTCP",
449                         0, KEY_READ, &key);
450                 if(ret != ERROR_SUCCESS)
451                         return 0;
452         }
453
454         str_domain = reg_readString(key, "DhcpDomain");
455         if(!str_domain)
456                 str_domain = reg_readString(key, "Domain");
457         str_nameserver = reg_readString(key, "DhcpNameServer");
458         if(!str_nameserver)
459                 str_nameserver = reg_readString(key, "NameServer");
460         str_searchlist = reg_readString(key, "SearchList");
461
462         RegCloseKey(key);
463
464         list_nameserver = 0;
465         if(str_nameserver)
466         {
467                 list_nameserver = string_split(str_nameserver, sep);
468                 jdns_string_delete(str_nameserver);
469         }
470         list_searchlist = 0;
471         if(str_searchlist)
472         {
473                 // lowercase the string
474                 jdns_string_t *p = string_tolower(str_searchlist);
475                 jdns_string_delete(str_searchlist);
476                 str_searchlist = p;
477
478                 list_searchlist = string_split(str_searchlist, sep);
479                 jdns_string_delete(str_searchlist);
480         }
481
482         params = jdns_dnsparams_new();
483         if(list_nameserver)
484         {
485                 // qt seems to do a strange thing here by running each name
486                 //   server address through the q3dns setLabel function, and
487                 //   then pulls the result as a list of addresses.  i have
488                 //   no idea why they do this, or how one IP address would
489                 //   turn into anything else, let alone several addresses.
490                 // so, uh, we're not going to do that.
491                 for(n = 0; n < list_nameserver->count; ++n)
492                 {
493                         jdns_address_t *addr = jdns_address_new();
494                         if(jdns_address_set_cstr(addr, (char *)list_nameserver->item[n]->data))
495                                 jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
496                         jdns_address_delete(addr);
497                 }
498                 jdns_stringlist_delete(list_nameserver);
499         }
500         if(str_domain)
501         {
502                 if(str_domain->size > 0)
503                         jdns_dnsparams_append_domain(params, str_domain);
504                 jdns_string_delete(str_domain);
505         }
506         if(list_searchlist)
507         {
508                 for(n = 0; n < list_searchlist->count; ++n)
509                 {
510                         if(list_searchlist->item[n]->size > 0)
511                                 jdns_dnsparams_append_domain(params, list_searchlist->item[n]);
512                 }
513                 jdns_stringlist_delete(list_searchlist);
514         }
515
516         return params;
517 }
518
519 static jdns_dnsparams_t *dnsparams_get_winsys()
520 {
521         jdns_dnsparams_t *params;
522         GetNetworkParamsFunc myGetNetworkParams;
523         DWORD ret;
524         HINSTANCE lib;
525         jdns_address_t *addr;
526         jdns_string_t *str;
527         IP_ADDR_STRING *ipstr;
528
529         lib = LoadLibraryA("iphlpapi");
530         if(!lib)
531                 return 0;
532
533         params = 0;
534         myGetNetworkParams = (GetNetworkParamsFunc)GetProcAddress(lib, "GetNetworkParams");
535         if(myGetNetworkParams)
536         {
537                 ULONG bufsize = 0;
538                 ret = myGetNetworkParams(0, &bufsize);
539                 if(ret == ERROR_BUFFER_OVERFLOW)
540                 {
541                         FIXED_INFO *info = (FIXED_INFO *)jdns_alloc((int)bufsize);
542                         ret = myGetNetworkParams(info, &bufsize);
543                         if(ret == ERROR_SUCCESS)
544                         {
545                                 params = jdns_dnsparams_new();
546                                 ipstr = &info->DnsServerList;
547                                 while(ipstr)
548                                 {
549                                         addr = jdns_address_new();
550                                         if(jdns_address_set_cstr(addr, (char *)ipstr->IpAddress.String))
551                                                 jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
552                                         jdns_address_delete(addr);
553                                         ipstr = ipstr->Next;
554                                 }
555                                 str = jdns_string_new();
556                                 jdns_string_set_cstr(str, info->DomainName);
557                                 if(str->size > 0)
558                                         jdns_dnsparams_append_domain(params, str);
559                                 jdns_string_delete(str);
560                         }
561                         jdns_free(info);
562                 }
563         }
564         FreeLibrary(lib);
565         return params;
566 }
567
568 static void apply_hosts_var_filepath(jdns_dnsparams_t *a, const char *envvar, const char *path)
569 {
570         jdns_string_t *e;
571         char *str;
572         int elen, plen;
573
574         e = jdns_getenv(envvar);
575         if(!e)
576                 return;
577         elen = strlen((char *)e->data);
578         plen = strlen(path);
579         str = (char *)jdns_alloc(elen + plen + 1);
580         memcpy(str, e->data, elen);
581         jdns_string_delete(e);
582
583         jdns_strcpy(str + elen, path);
584         apply_hosts_file(a, str);
585         jdns_free(str);
586 }
587
588 static void apply_win_hosts_file(jdns_dnsparams_t *a)
589 {
590         // windows 64-bit
591         apply_hosts_var_filepath(a, "SystemRoot", "\\SysWOW64\\drivers\\etc\\hosts");
592
593         // winnt+
594         apply_hosts_var_filepath(a, "SystemRoot", "\\system32\\drivers\\etc\\hosts");
595
596         // win9x
597         apply_hosts_var_filepath(a, "WINDIR", "\\hosts");
598 }
599
600 static jdns_dnsparams_t *dnsparams_get_win()
601 {
602         int n;
603         jdns_dnsparams_t *sys_params, *reg_params;
604
605         reg_params = dnsparams_get_winreg();
606         sys_params = dnsparams_get_winsys();
607
608         // no sys params?  take the reg params then
609         if(!sys_params)
610         {
611                 apply_win_hosts_file(reg_params);
612                 return reg_params;
613         }
614
615         // sys params don't have a search list, so merge the domains from
616         //   the registry if possible
617         if(reg_params)
618         {
619                 for(n = 0; n < reg_params->domains->count; ++n)
620                 {
621                         jdns_string_t *reg_str = reg_params->domains->item[n];
622
623                         // don't add dups
624                         if(!dnsparams_have_domain(sys_params, reg_str))
625                                 jdns_dnsparams_append_domain(sys_params, reg_str);
626                 }
627                 jdns_dnsparams_delete(reg_params);
628         }
629         apply_win_hosts_file(sys_params);
630         return sys_params;
631 }
632
633 #endif
634
635 #ifdef JDNS_OS_UNIX
636
637 static jdns_dnsparams_t *dnsparams_get_unixfiles()
638 {
639         FILE *f;
640         int n;
641         jdns_dnsparams_t *params;
642         jdns_string_t *line, *simp;
643         jdns_stringlist_t *parts;
644
645         params = jdns_dnsparams_new();
646
647         f = jdns_fopen("/etc/resolv.conf", "r");
648         if(!f)
649                 return params;
650         while(1)
651         {
652                 line = file_nextline(f);
653                 if(!line)
654                         break;
655
656                 // truncate at comment
657                 n = string_indexOf(line, '#', 0);
658                 if(n != -1)
659                 {
660                         line->size = n;
661                         line->data[n] = 0;
662                 }
663
664                 simp = string_simplify(line);
665                 jdns_string_delete(line);
666
667                 parts = string_split(simp, ' ');
668                 jdns_string_delete(simp);
669
670                 if(parts->count < 2)
671                 {
672                         jdns_stringlist_delete(parts);
673                         continue;
674                 }
675
676                 simp = string_tolower(parts->item[0]);
677                 if(strcmp((char *)simp->data, "nameserver") == 0)
678                 {
679                         jdns_address_t *addr = jdns_address_new();
680                         jdns_address_set_cstr(addr, (const char *)parts->item[1]->data);
681                         jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
682                         jdns_address_delete(addr);
683                 }
684                 else if(strcmp((char *)simp->data, "search") == 0)
685                 {
686                         for(n = 1; n < parts->count; ++n)
687                         {
688                                 jdns_dnsparams_append_domain(params, parts->item[n]);
689                         }
690                 }
691                 else if(strcmp((char *)simp->data, "domain") == 0)
692                 {
693                         jdns_dnsparams_append_domain(params, parts->item[1]);
694                 }
695                 jdns_string_delete(simp);
696
697                 jdns_stringlist_delete(parts);
698         }
699         fclose(f);
700         return params;
701 }
702
703 #if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3)))
704 # define JDNS_MODERN_RES_API
705 #endif
706
707 #ifndef JDNS_MODERN_RES_API
708 typedef int (*res_init_func)();
709 static int my_res_init()
710 {
711 #ifdef JDNS_OS_MAC
712         res_init_func mac_res_init;
713
714         // look up res_init in the system library (qt does this, not sure why)
715         mac_res_init = (res_init_func)dlsym(RTLD_NEXT, "res_init");
716         if(!mac_res_init)
717                 return -1;
718         return mac_res_init();
719 #else
720         return res_init();
721 #endif
722 }
723 #endif
724
725 // on some platforms, __res_state_ext exists as a struct but it is not
726 //   a define, so the #ifdef doesn't work.  as a workaround, we'll explicitly
727 //   specify the platforms that have __res_state_ext
728 //#ifdef __res_state_ext
729 #if defined(JDNS_OS_MAC) || defined(JDNS_OS_FREEBSD) || \
730     defined(JDNS_OS_NETBSD) || defined (JDNS_OS_SOLARIS)
731 # define USE_EXTEXT
732 #endif
733
734 static jdns_dnsparams_t *dnsparams_get_unixsys()
735 {
736         int n;
737         jdns_dnsparams_t *params;
738
739 #ifdef JDNS_MODERN_RES_API
740         struct __res_state res;
741         memset(&res, 0, sizeof(struct __res_state));
742         n = res_ninit(&res);
743 #define RESVAR res
744 #else
745         n = my_res_init();
746 #define RESVAR _res
747 #endif
748
749         params = jdns_dnsparams_new();
750
751         // error initializing?
752         if(n == -1)
753                 return params;
754
755         // nameservers - ipv6
756         for(n = 0; n < MAXNS && n < RESVAR._u._ext.nscount; ++n)
757         {
758                 jdns_address_t *addr;
759                 struct sockaddr_in6 *sa6;
760
761 #ifdef USE_EXTEXT
762                 // seems _ext.ext can be null in some cases...
763                 if(RESVAR._u._ext.ext == NULL)
764                         break;
765
766                 sa6 = ((struct sockaddr_in6 *)RESVAR._u._ext.ext) + n;
767 #else
768                 sa6 = RESVAR._u._ext.nsaddrs[n];
769 #endif
770
771                 if(sa6 == NULL)
772                         continue;
773                 addr = jdns_address_new();
774                 jdns_address_set_ipv6(addr, sa6->sin6_addr.s6_addr);
775                 jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
776                 jdns_address_delete(addr);
777         }
778
779         // nameservers - ipv4
780         for(n = 0; n < MAXNS && n < RESVAR.nscount; ++n)
781         {
782                 jdns_address_t *addr = jdns_address_new();
783                 jdns_address_set_ipv4(addr, ntohl(RESVAR.nsaddr_list[n].sin_addr.s_addr));
784                 jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
785                 jdns_address_delete(addr);
786         }
787
788         // domain name
789         if(strlen(RESVAR.defdname) > 0)
790         {
791                 jdns_string_t *str;
792                 jdns_string_t *p;
793                 str = jdns_string_new();
794                 jdns_string_set_cstr(str, RESVAR.defdname);
795                 p = string_tolower(str);
796                 jdns_string_delete(str);
797                 str = p;
798                 jdns_dnsparams_append_domain(params, str);
799                 jdns_string_delete(str);
800         }
801
802         // search list
803 #ifdef MAXDFLSRCH
804         for(n = 0; n < MAXDFLSRCH && RESVAR.dnsrch[n]; ++n)
805         {
806                 if(strlen(RESVAR.dnsrch[n]) > 0)
807                 {
808                         jdns_string_t *str;
809                         jdns_string_t *p;
810                         str = jdns_string_new();
811                         jdns_string_set_cstr(str, RESVAR.dnsrch[n]);
812                         p = string_tolower(str);
813                         jdns_string_delete(str);
814                         str = p;
815
816                         // don't add dups
817                         if(!dnsparams_have_domain(params, str))
818                                 jdns_dnsparams_append_domain(params, str);
819
820                         jdns_string_delete(str);
821                 }
822         }
823 #endif
824
825         return params;
826 }
827
828 static jdns_dnsparams_t *dnsparams_get_unix()
829 {
830         jdns_dnsparams_t *params;
831
832         // prefer system calls over files
833         params = dnsparams_get_unixsys();
834         if(params->nameservers->count == 0)
835         {
836                 jdns_dnsparams_delete(params);
837                 params = dnsparams_get_unixfiles();
838         }
839
840         apply_hosts_file(params, "/etc/hosts");
841
842         return params;
843 }
844
845 #endif
846
847 jdns_dnsparams_t *jdns_system_dnsparams()
848 {
849 #ifdef JDNS_OS_WIN
850         return dnsparams_get_win();
851 #else
852         return dnsparams_get_unix();
853 #endif
854 }