2
* Copyright (C) 2005-2008 Justin Karneges
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:
12
* The above copyright notice and this permission notice shall be included
13
* in all copies or substantial portions of the Software.
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.
25
this code probes the system for dns settings. blah.
32
domain name, name server, "search list" found in windows registry here:
35
System\CurrentControlSet\Services\Tcpip\Parameters <-- win nt+
36
System\CurrentControlSet\Services\VxD\MSTCP <-- win 98
38
for domain, try DhcpDomain else Domain
39
for name servers, try DhcpNameServer, else NameServer
40
for search list, try SearchList
42
iphlpapi.dll : GetNetworkParams(PFIXED_INFO, PULONG);
45
info->DnsServerList (if not null, use it, and loop through ->Next until
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.
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
59
qt doesn't read the hosts file on windows. this might be a good idea, but
60
probably not worth it.
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
70
if the first item is "domain", then the next item should be added to the
72
do case-insensitive matching for the item types
73
for search/domain, the items are in the 8-bit system locale
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:
79
#if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2)
80
&& (__GLIBC_MINOR__ >= 3)))
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.
88
under a modern res api, we do:
89
struct __res_state 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.
94
read the res struct to obtain the name servers, search list, and domain.
95
lowercase the search list and domain.
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.
100
read /etc/hosts manually:
102
if there is a '#' character in the line, remove it and everything to
106
split the line at spaces
107
first item is the ip address
108
all remaining items are hostnames
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)
117
# include <windows.h>
121
# include <netinet/in.h>
122
# include <arpa/nameser.h>
127
#define string_indexOf jdns_string_indexOf
128
#define string_split jdns_string_split
130
static int char_isspace(unsigned char c)
132
if(c == ' ' || c == '\t' || c == '\n' || c == '\r')
137
static unsigned char *string_getnextword(unsigned char *in, int size, int pos, int *newpos)
146
// skip any space at the start
147
while(at < size && char_isspace(in[at]))
150
// all space? no word then
154
// skip until a space or end
156
while(n < size && !char_isspace(in[n]))
160
// allocate length + zero byte
161
out = (unsigned char *)jdns_alloc(len + 1);
164
memcpy(out, in + at, len);
170
static jdns_string_t *string_simplify(const jdns_string_t *in)
177
jdns_string_t *outstr;
178
jdns_stringlist_t *wordlist;
180
// gather words and total of lengths
183
wordlist = jdns_stringlist_new();
187
unsigned char *str = string_getnextword(in->data, in->size, pos, &pos);
190
word = jdns_string_new();
191
jdns_string_set_cstr(word, (char *)str);
193
jdns_stringlist_append(wordlist, word);
195
jdns_string_delete(word);
200
jdns_stringlist_delete(wordlist);
202
outstr = jdns_string_new();
203
jdns_string_set_cstr(outstr, "");
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);
213
for(n = 0; n < wordlist->count; ++n)
215
unsigned char *data = wordlist->item[n]->data;
216
int size = wordlist->item[n]->size;
217
memcpy(out + pos, data, size);
220
// if this is not the last word, append a space
221
if(n + 1 < wordlist->count)
224
jdns_stringlist_delete(wordlist);
226
outstr = jdns_string_new();
227
jdns_string_set(outstr, out, outlen);
232
static jdns_string_t *string_tolower(const jdns_string_t *in)
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]);
241
static jdns_string_t *file_nextline(FILE *f)
248
buf = (unsigned char *)jdns_alloc(size);
252
unsigned char c = fgetc(f);
257
// if we read at least one char, take it as a
275
str = jdns_string_new();
276
jdns_string_set(str, buf, at);
281
static jdns_dnshostlist_t *read_hosts_file(const char *path)
283
jdns_dnshostlist_t *out;
285
jdns_string_t *line, *simp;
286
jdns_stringlist_t *parts;
287
jdns_address_t *addr;
290
out = jdns_dnshostlist_new();
292
f = jdns_fopen(path, "r");
297
line = file_nextline(f);
301
// truncate at comment
302
n = string_indexOf(line, '#', 0);
309
simp = string_simplify(line);
310
jdns_string_delete(line);
312
parts = string_split(simp, ' ');
313
jdns_string_delete(simp);
317
jdns_stringlist_delete(parts);
321
addr = jdns_address_new();
322
if(!jdns_address_set_cstr(addr, (const char *)parts->item[0]->data))
324
jdns_address_delete(addr);
325
jdns_stringlist_delete(parts);
329
for(n = 1; n < parts->count; ++n)
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);
338
jdns_address_delete(addr);
339
jdns_stringlist_delete(parts);
345
static void apply_hosts_file(jdns_dnsparams_t *a, const char *path)
348
jdns_dnshostlist_t *list;
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);
356
static int dnsparams_have_domain(const jdns_dnsparams_t *a, const jdns_string_t *domain)
359
for(n = 0; n < a->domains->count; ++n)
361
jdns_string_t *str = a->domains->item[n];
362
if(strcmp((const char *)str->data, (const char *)domain->data) == 0)
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
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;
383
} IP_ADDR_STRING, *PIP_ADDR_STRING;
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;
390
char ScopeId[MAX_SCOPE_ID_LEN + 4];
394
} FIXED_INFO, *PFIXED_INFO;
397
typedef DWORD (WINAPI *GetNetworkParamsFunc)(PFIXED_INFO, PULONG);
399
static jdns_string_t *reg_readString(HKEY hk, const char *subkey)
404
jdns_string_t *str = 0;
407
buf = (char *)jdns_alloc((int)bufsize);
410
ret = RegQueryValueExA(hk, subkey, 0, 0, (LPBYTE)buf, &bufsize);
411
if(ret == ERROR_MORE_DATA)
413
buf = (char *)jdns_realloc(buf, bufsize);
419
ret = RegQueryValueExA(hk, subkey, 0, 0, (LPBYTE)buf, &bufsize);
421
if(ret == ERROR_SUCCESS)
423
str = jdns_string_new();
424
jdns_string_set_cstr(str, (char *)buf);
430
static jdns_dnsparams_t *dnsparams_get_winreg()
433
jdns_dnsparams_t *params;
437
jdns_string_t *str_domain, *str_nameserver, *str_searchlist;
438
jdns_stringlist_t *list_nameserver, *list_searchlist;
441
ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
442
"System\\CurrentControlSet\\Services\\Tcpip\\Parameters",
444
if(ret != ERROR_SUCCESS)
447
ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
448
"System\\CurrentControlSet\\Services\\VxD\\MSTCP",
450
if(ret != ERROR_SUCCESS)
454
str_domain = reg_readString(key, "DhcpDomain");
456
str_domain = reg_readString(key, "Domain");
457
str_nameserver = reg_readString(key, "DhcpNameServer");
459
str_nameserver = reg_readString(key, "NameServer");
460
str_searchlist = reg_readString(key, "SearchList");
467
list_nameserver = string_split(str_nameserver, sep);
468
jdns_string_delete(str_nameserver);
473
// lowercase the string
474
jdns_string_t *p = string_tolower(str_searchlist);
475
jdns_string_delete(str_searchlist);
478
list_searchlist = string_split(str_searchlist, sep);
479
jdns_string_delete(str_searchlist);
482
params = jdns_dnsparams_new();
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)
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);
498
jdns_stringlist_delete(list_nameserver);
502
if(str_domain->size > 0)
503
jdns_dnsparams_append_domain(params, str_domain);
504
jdns_string_delete(str_domain);
508
for(n = 0; n < list_searchlist->count; ++n)
510
if(list_searchlist->item[n]->size > 0)
511
jdns_dnsparams_append_domain(params, list_searchlist->item[n]);
513
jdns_stringlist_delete(list_searchlist);
519
static jdns_dnsparams_t *dnsparams_get_winsys()
521
jdns_dnsparams_t *params;
522
GetNetworkParamsFunc myGetNetworkParams;
525
jdns_address_t *addr;
527
IP_ADDR_STRING *ipstr;
529
lib = LoadLibraryA("iphlpapi");
534
myGetNetworkParams = (GetNetworkParamsFunc)GetProcAddress(lib, "GetNetworkParams");
535
if(myGetNetworkParams)
538
ret = myGetNetworkParams(0, &bufsize);
539
if(ret == ERROR_BUFFER_OVERFLOW)
541
FIXED_INFO *info = (FIXED_INFO *)jdns_alloc((int)bufsize);
542
ret = myGetNetworkParams(info, &bufsize);
543
if(ret == ERROR_SUCCESS)
545
params = jdns_dnsparams_new();
546
ipstr = &info->DnsServerList;
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);
555
str = jdns_string_new();
556
jdns_string_set_cstr(str, info->DomainName);
558
jdns_dnsparams_append_domain(params, str);
559
jdns_string_delete(str);
568
static void apply_hosts_var_filepath(jdns_dnsparams_t *a, const char *envvar, const char *path)
574
e = jdns_getenv(envvar);
577
elen = strlen((char *)e->data);
579
str = (char *)jdns_alloc(elen + plen + 1);
580
memcpy(str, e->data, elen);
581
jdns_string_delete(e);
583
jdns_strcpy(str + elen, path);
584
apply_hosts_file(a, str);
588
static void apply_win_hosts_file(jdns_dnsparams_t *a)
591
apply_hosts_var_filepath(a, "SystemRoot", "\\SysWOW64\\drivers\\etc\\hosts");
594
apply_hosts_var_filepath(a, "SystemRoot", "\\system32\\drivers\\etc\\hosts");
597
apply_hosts_var_filepath(a, "WINDIR", "\\hosts");
600
static jdns_dnsparams_t *dnsparams_get_win()
603
jdns_dnsparams_t *sys_params, *reg_params;
605
reg_params = dnsparams_get_winreg();
606
sys_params = dnsparams_get_winsys();
608
// no sys params? take the reg params then
611
apply_win_hosts_file(reg_params);
615
// sys params don't have a search list, so merge the domains from
616
// the registry if possible
619
for(n = 0; n < reg_params->domains->count; ++n)
621
jdns_string_t *reg_str = reg_params->domains->item[n];
624
if(!dnsparams_have_domain(sys_params, reg_str))
625
jdns_dnsparams_append_domain(sys_params, reg_str);
627
jdns_dnsparams_delete(reg_params);
629
apply_win_hosts_file(sys_params);
637
static jdns_dnsparams_t *dnsparams_get_unixfiles()
641
jdns_dnsparams_t *params;
642
jdns_string_t *line, *simp;
643
jdns_stringlist_t *parts;
645
params = jdns_dnsparams_new();
647
f = jdns_fopen("/etc/resolv.conf", "r");
652
line = file_nextline(f);
656
// truncate at comment
657
n = string_indexOf(line, '#', 0);
664
simp = string_simplify(line);
665
jdns_string_delete(line);
667
parts = string_split(simp, ' ');
668
jdns_string_delete(simp);
672
jdns_stringlist_delete(parts);
676
simp = string_tolower(parts->item[0]);
677
if(strcmp((char *)simp->data, "nameserver") == 0)
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);
684
else if(strcmp((char *)simp->data, "search") == 0)
686
for(n = 1; n < parts->count; ++n)
688
jdns_dnsparams_append_domain(params, parts->item[n]);
691
else if(strcmp((char *)simp->data, "domain") == 0)
693
jdns_dnsparams_append_domain(params, parts->item[1]);
695
jdns_string_delete(simp);
697
jdns_stringlist_delete(parts);
703
#if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3)))
704
# define JDNS_MODERN_RES_API
707
#ifndef JDNS_MODERN_RES_API
708
typedef int (*res_init_func)();
709
static int my_res_init()
712
res_init_func mac_res_init;
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");
718
return mac_res_init();
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)
734
static jdns_dnsparams_t *dnsparams_get_unixsys()
737
jdns_dnsparams_t *params;
739
#ifdef JDNS_MODERN_RES_API
740
struct __res_state res;
741
memset(&res, 0, sizeof(struct __res_state));
749
params = jdns_dnsparams_new();
751
// error initializing?
755
// nameservers - ipv6
757
for(n = 0; n < MAXNS && n < RESVAR._u._ext.nscount6; ++n)
759
for(n = 0; n < MAXNS && n < RESVAR._u._ext.nscount; ++n)
762
jdns_address_t *addr;
763
struct sockaddr_in6 *sa6;
766
// seems _ext.ext can be null in some cases...
767
if(RESVAR._u._ext.ext == NULL)
770
sa6 = ((struct sockaddr_in6 *)RESVAR._u._ext.ext) + n;
772
sa6 = RESVAR._u._ext.nsaddrs[n];
777
addr = jdns_address_new();
778
jdns_address_set_ipv6(addr, sa6->sin6_addr.s6_addr);
779
jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
780
jdns_address_delete(addr);
783
// nameservers - ipv4
784
for(n = 0; n < MAXNS && n < RESVAR.nscount; ++n)
786
jdns_address_t *addr = jdns_address_new();
787
jdns_address_set_ipv4(addr, ntohl(RESVAR.nsaddr_list[n].sin_addr.s_addr));
788
jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
789
jdns_address_delete(addr);
793
if(strlen(RESVAR.defdname) > 0)
797
str = jdns_string_new();
798
jdns_string_set_cstr(str, RESVAR.defdname);
799
p = string_tolower(str);
800
jdns_string_delete(str);
802
jdns_dnsparams_append_domain(params, str);
803
jdns_string_delete(str);
808
for(n = 0; n < MAXDFLSRCH && RESVAR.dnsrch[n]; ++n)
810
if(strlen(RESVAR.dnsrch[n]) > 0)
814
str = jdns_string_new();
815
jdns_string_set_cstr(str, RESVAR.dnsrch[n]);
816
p = string_tolower(str);
817
jdns_string_delete(str);
821
if(!dnsparams_have_domain(params, str))
822
jdns_dnsparams_append_domain(params, str);
824
jdns_string_delete(str);
832
static jdns_dnsparams_t *dnsparams_get_unix()
834
jdns_dnsparams_t *params;
836
// prefer system calls over files
837
params = dnsparams_get_unixsys();
838
if(params->nameservers->count == 0)
840
jdns_dnsparams_delete(params);
841
params = dnsparams_get_unixfiles();
844
apply_hosts_file(params, "/etc/hosts");
851
jdns_dnsparams_t *jdns_system_dnsparams()
854
return dnsparams_get_win();
856
return dnsparams_get_unix();