This repository has been archived on 2025-12-24. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
yachat/iris-legacy/iris/irisnet/jdns/jdns_sys.c
2025-12-25 01:38:25 +05:00

822 lines
19 KiB
C

/*
* Copyright (C) 2005,2006 Justin Karneges
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
this code probes the system for dns settings. blah.
q3dns strategies
----------------
windows:
domain name, name server, "search list" found in windows registry here:
HKEY_LOCAL_MACHINE
System\CurrentControlSet\Services\Tcpip\Parameters <-- win nt+
System\CurrentControlSet\Services\VxD\MSTCP <-- win 98
for domain, try DhcpDomain else Domain
for name servers, try DhcpNameServer, else NameServer
for search list, try SearchList
iphlpapi.dll : GetNetworkParams(PFIXED_INFO, PULONG);
info->DomainName
info->DnsServerList (if not null, use it, and loop through ->Next until
null)
no search list
first try getnetworkparams. if that fails, try the nt regkey then the 98
regkey. it seems that search list can only come from the registry, so
maybe we need to grab that entry even if getnetworkparams works.
in the case of the registry, the nameserver and searchlist entries are
delimited by spaces on win nt and commas on win 98. probably a good
idea to simplify white space first (chop away space at start and end,
reduce all sections of spaces to one char). also, lowercase the search
list.
qt doesn't read the hosts file on windows. this might be a good idea, but
probably not worth it.
unix:
read /etc/resolv.conf manually:
for each line, split at spaces
if the first item is "nameserver", then there should be an IP address
following it. note: may contain mixed ipv4 and ipv6 addresses
if the first item is "search", all other items are added to the domain
list
if the first item is "domain", then the next item should be added to the
domain list.
do case-insensitive matching for the item types
for search/domain, the items are in the 8-bit system locale
info can also be fetched using system calls. we use the res_* stuff here.
first we should detect for a "modern res api". this is available from
glibc 2.3 and onward. use the following scheme to check for it:
#if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2)
&& (__GLIBC_MINOR__ >= 3)))
// modern res api
#endif
on mac we should look up res_init in the system library. see:
qt_mac_resolve_sys(RTLD_NEXT, "res_init"); for a hint.
otherwise we can just use res_init() straight.
under a modern res api, we do:
struct __res_state res;
res_ninit(&res);
otherwise, we simply call res_init(). for the modern api, we use the "res"
struct that we made. otherwise, we use the global "_res" struct.
read the res struct to obtain the name servers, search list, and domain.
lowercase the search list and domain.
qt tries the file, and if that fails it tries the syscalls. we may want to
do the syscalls first, or even just do both all the time.
read /etc/hosts manually:
for each line
if there is a '#' character in the line, remove it and everything to
the right
simplify white space
convert to lowercase
split the line at spaces
first item is the ip address
all remaining items are hostnames
note: these hosts could also be used for reverse-dns too
note2: Windows has a hosts file as well (like C:\WINDOWS\hosts)
*/
#include "jdns_p.h"
#ifdef JDNS_OS_WIN
# include <windows.h>
#endif
#ifdef JDNS_OS_UNIX
# include <netinet/in.h>
# include <arpa/nameser.h>
# include <resolv.h>
# include <dlfcn.h>
#endif
#define string_indexOf jdns_string_indexOf
#define string_split jdns_string_split
static int char_isspace(unsigned char c)
{
if(c == ' ' || c == '\t' || c == '\n' || c == '\r')
return 1;
return 0;
}
static unsigned char *string_getnextword(unsigned char *in, int size, int pos, int *newpos)
{
int n;
int at;
int len;
unsigned char *out;
at = pos;
// skip any space at the start
while(at < size && char_isspace(in[at]))
++at;
// all space? no word then
if(at >= size)
return 0;
// skip until a space or end
n = at;
while(n < size && !char_isspace(in[n]))
++n;
len = n - at;
// allocate length + zero byte
out = (unsigned char *)jdns_alloc(len + 1);
if(!out)
return 0;
memcpy(out, in + at, len);
out[len] = 0;
*newpos = at + len;
return out;
}
static jdns_string_t *string_simplify(const jdns_string_t *in)
{
int n;
int pos;
int total;
unsigned char *out;
int outlen;
jdns_string_t *outstr;
jdns_stringlist_t *wordlist;
// gather words and total of lengths
pos = 0;
total = 0;
wordlist = jdns_stringlist_new();
while(1)
{
jdns_string_t *word;
unsigned char *str = string_getnextword(in->data, in->size, pos, &pos);
if(!str)
break;
word = jdns_string_new();
jdns_string_set_cstr(word, (char *)str);
jdns_free(str);
jdns_stringlist_append(wordlist, word);
total += word->size;
jdns_string_delete(word);
}
if(total == 0)
{
jdns_stringlist_delete(wordlist);
outstr = jdns_string_new();
jdns_string_set_cstr(outstr, "");
return outstr;
}
// we need to allocate space for total lengths and wordcount-1 spaces
outlen = total + (wordlist->count - 1);
out = (unsigned char *)jdns_alloc(outlen);
// lay out the words
pos = 0;
for(n = 0; n < wordlist->count; ++n)
{
unsigned char *data = wordlist->item[n]->data;
int size = wordlist->item[n]->size;
memcpy(out + pos, data, size);
pos += size;
// if this is not the last word, append a space
if(n + 1 < wordlist->count)
out[pos++] = ' ';
}
jdns_stringlist_delete(wordlist);
outstr = jdns_string_new();
jdns_string_set(outstr, out, outlen);
jdns_free(out);
return outstr;
}
static jdns_string_t *string_tolower(const jdns_string_t *in)
{
int n;
jdns_string_t *out = jdns_string_copy(in);
for(n = 0; n < out->size; ++n)
out->data[n] = tolower(out->data[n]);
return out;
}
static jdns_string_t *file_nextline(FILE *f)
{
int at, size;
unsigned char *buf;
jdns_string_t *str;
size = 1023;
buf = (unsigned char *)jdns_alloc(size);
at = 0;
while(1)
{
unsigned char c = fgetc(f);
if(feof(f))
{
jdns_free(buf);
return 0;
}
if(c == '\n')
break;
if(c == '\r')
continue;
if(at < 1023)
buf[at++] = c;
}
str = jdns_string_new();
jdns_string_set(str, buf, at);
jdns_free(buf);
return str;
}
static jdns_dnshostlist_t *read_hosts_file(const char *path)
{
jdns_dnshostlist_t *out;
FILE *f;
jdns_string_t *line, *simp;
jdns_stringlist_t *parts;
jdns_address_t *addr;
int n;
out = jdns_dnshostlist_new();
f = fopen(path, "r");
if(!f)
return out;
while(1)
{
line = file_nextline(f);
if(!line)
break;
// truncate at comment
n = string_indexOf(line, '#', 0);
if(n != -1)
{
line->size = n;
line->data[n] = 0;
}
simp = string_simplify(line);
jdns_string_delete(line);
parts = string_split(simp, ' ');
jdns_string_delete(simp);
if(parts->count < 2)
{
jdns_stringlist_delete(parts);
continue;
}
addr = jdns_address_new();
if(!jdns_address_set_cstr(addr, (const char *)parts->item[0]->data))
{
jdns_address_delete(addr);
jdns_stringlist_delete(parts);
continue;
}
for(n = 1; n < parts->count; ++n)
{
jdns_dnshost_t *h = jdns_dnshost_new();
h->name = jdns_string_copy(parts->item[n]);
h->address = jdns_address_copy(addr);
jdns_dnshostlist_append(out, h);
jdns_dnshost_delete(h);
}
jdns_address_delete(addr);
jdns_stringlist_delete(parts);
}
fclose(f);
return out;
}
static void apply_hosts_file(jdns_dnsparams_t *a, const char *path)
{
int n;
jdns_dnshostlist_t *list;
list = read_hosts_file(path);
for(n = 0; n < list->count; ++n)
jdns_dnshostlist_append(a->hosts, list->item[n]);
jdns_dnshostlist_delete(list);
}
static int dnsparams_have_domain(const jdns_dnsparams_t *a, const jdns_string_t *domain)
{
int n;
for(n = 0; n < a->domains->count; ++n)
{
jdns_string_t *str = a->domains->item[n];
if(strcmp((const char *)str->data, (const char *)domain->data) == 0)
return 1;
}
return 0;
}
#ifdef JDNS_OS_WIN
// from Microsoft IPTypes.h
#ifndef IP_TYPES_INCLUDED
#define MAX_HOSTNAME_LEN 128
#define MAX_DOMAIN_NAME_LEN 128
#define MAX_SCOPE_ID_LEN 256
typedef struct {
char String[4 * 4];
} IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
typedef struct _IP_ADDR_STRING {
struct _IP_ADDR_STRING* Next;
IP_ADDRESS_STRING IpAddress;
IP_MASK_STRING IpMask;
DWORD Context;
} IP_ADDR_STRING, *PIP_ADDR_STRING;
typedef struct {
char HostName[MAX_HOSTNAME_LEN + 4] ;
char DomainName[MAX_DOMAIN_NAME_LEN + 4];
PIP_ADDR_STRING CurrentDnsServer;
IP_ADDR_STRING DnsServerList;
UINT NodeType;
char ScopeId[MAX_SCOPE_ID_LEN + 4];
UINT EnableRouting;
UINT EnableProxy;
UINT EnableDns;
} FIXED_INFO, *PFIXED_INFO;
#endif
typedef DWORD (WINAPI *GetNetworkParamsFunc)(PFIXED_INFO, PULONG);
static jdns_string_t *reg_readString(HKEY hk, const char *subkey)
{
char *buf;
DWORD bufsize;
int ret;
jdns_string_t *str = 0;
bufsize = 1024;
buf = (char *)jdns_alloc((int)bufsize);
if(!buf)
return 0;
ret = RegQueryValueExA(hk, subkey, 0, 0, (LPBYTE)buf, &bufsize);
if(ret == ERROR_MORE_DATA)
{
buf = (char *)jdns_realloc(buf, bufsize);
if(!buf)
{
jdns_free(buf);
return 0;
}
ret = RegQueryValueExA(hk, subkey, 0, 0, (LPBYTE)buf, &bufsize);
}
if(ret == ERROR_SUCCESS)
{
str = jdns_string_new();
jdns_string_set_cstr(str, (char *)buf);
}
jdns_free(buf);
return str;
}
static jdns_dnsparams_t *dnsparams_get_winreg()
{
int n;
jdns_dnsparams_t *params;
HKEY key;
int ret;
char sep;
jdns_string_t *str_domain, *str_nameserver, *str_searchlist;
jdns_stringlist_t *list_nameserver, *list_searchlist;
sep = ' ';
ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Services\\Tcpip\\Parameters",
0, KEY_READ, &key);
if(ret != ERROR_SUCCESS)
{
sep = ',';
ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Services\\VxD\\MSTCP",
0, KEY_READ, &key);
if(ret != ERROR_SUCCESS)
return 0;
}
str_domain = reg_readString(key, "DhcpDomain");
if(!str_domain)
str_domain = reg_readString(key, "Domain");
str_nameserver = reg_readString(key, "DhcpNameServer");
if(!str_nameserver)
str_nameserver = reg_readString(key, "NameServer");
str_searchlist = reg_readString(key, "SearchList");
RegCloseKey(key);
list_nameserver = 0;
if(str_nameserver)
{
list_nameserver = string_split(str_nameserver, sep);
jdns_string_delete(str_nameserver);
}
list_searchlist = 0;
if(str_searchlist)
{
// lowercase the string
jdns_string_t *p = string_tolower(str_searchlist);
jdns_string_delete(str_searchlist);
str_searchlist = p;
list_searchlist = string_split(str_searchlist, sep);
jdns_string_delete(str_searchlist);
}
params = jdns_dnsparams_new();
if(list_nameserver)
{
// qt seems to do a strange thing here by running each name
// server address through the q3dns setLabel function, and
// then pulls the result as a list of addresses. i have
// no idea why they do this, or how one IP address would
// turn into anything else, let alone several addresses.
// so, uh, we're not going to do that.
for(n = 0; n < list_nameserver->count; ++n)
{
jdns_address_t *addr = jdns_address_new();
if(jdns_address_set_cstr(addr, (char *)list_nameserver->item[n]->data))
jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
jdns_address_delete(addr);
}
jdns_stringlist_delete(list_nameserver);
}
if(str_domain)
{
if(str_domain->size > 0)
jdns_dnsparams_append_domain(params, str_domain);
jdns_string_delete(str_domain);
}
if(list_searchlist)
{
for(n = 0; n < list_searchlist->count; ++n)
{
if(list_searchlist->item[n]->size > 0)
jdns_dnsparams_append_domain(params, list_searchlist->item[n]);
}
jdns_stringlist_delete(list_searchlist);
}
return params;
}
static jdns_dnsparams_t *dnsparams_get_winsys()
{
jdns_dnsparams_t *params;
GetNetworkParamsFunc myGetNetworkParams;
DWORD ret;
HINSTANCE lib;
jdns_address_t *addr;
jdns_string_t *str;
IP_ADDR_STRING *ipstr;
lib = LoadLibraryA("iphlpapi");
if(!lib)
return 0;
params = 0;
myGetNetworkParams = (GetNetworkParamsFunc)GetProcAddress(lib, "GetNetworkParams");
if(myGetNetworkParams)
{
ULONG bufsize = 0;
ret = myGetNetworkParams(0, &bufsize);
if(ret == ERROR_BUFFER_OVERFLOW)
{
FIXED_INFO *info = (FIXED_INFO *)jdns_alloc((int)bufsize);
ret = myGetNetworkParams(info, &bufsize);
if(ret == ERROR_SUCCESS)
{
params = jdns_dnsparams_new();
ipstr = &info->DnsServerList;
while(ipstr)
{
addr = jdns_address_new();
if(jdns_address_set_cstr(addr, (char *)ipstr->IpAddress.String))
jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
jdns_address_delete(addr);
ipstr = ipstr->Next;
}
str = jdns_string_new();
jdns_string_set_cstr(str, info->DomainName);
if(str->size > 0)
jdns_dnsparams_append_domain(params, str);
jdns_string_delete(str);
}
jdns_free(info);
}
}
FreeLibrary(lib);
return params;
}
static void apply_win_hosts_file(jdns_dnsparams_t *a)
{
char *p, *str;
int len;
p = getenv("WINDIR");
if(!p)
return;
len = strlen(p);
str = (char *)jdns_alloc(len + 100); // should be enough
memcpy(str, p, len);
strcpy(str + len, "\\system32\\drivers\\etc\\hosts"); // winnt+
apply_hosts_file(a, str);
strcpy(str + len, "\\hosts"); // win9x
apply_hosts_file(a, str);
jdns_free(str);
}
static jdns_dnsparams_t *dnsparams_get_win()
{
int n;
jdns_dnsparams_t *sys_params, *reg_params;
reg_params = dnsparams_get_winreg();
sys_params = dnsparams_get_winsys();
// no sys params? take the reg params then
if(!sys_params)
{
apply_win_hosts_file(reg_params);
return reg_params;
}
// sys params don't have a search list, so merge the domains from
// the registry if possible
if(reg_params)
{
for(n = 0; n < reg_params->domains->count; ++n)
{
jdns_string_t *reg_str = reg_params->domains->item[n];
// don't add dups
if(!dnsparams_have_domain(sys_params, reg_str))
jdns_dnsparams_append_domain(sys_params, reg_str);
}
jdns_dnsparams_delete(reg_params);
}
apply_win_hosts_file(sys_params);
return sys_params;
}
#endif
#ifdef JDNS_OS_UNIX
static jdns_dnsparams_t *dnsparams_get_unixfiles()
{
FILE *f;
int n;
jdns_dnsparams_t *params;
jdns_string_t *line, *simp;
jdns_stringlist_t *parts;
params = jdns_dnsparams_new();
f = fopen("/etc/resolv.conf", "r");
if(!f)
return params;
while(1)
{
line = file_nextline(f);
if(!line)
break;
// truncate at comment
n = string_indexOf(line, '#', 0);
if(n != -1)
{
line->size = n;
line->data[n] = 0;
}
simp = string_simplify(line);
jdns_string_delete(line);
parts = string_split(simp, ' ');
jdns_string_delete(simp);
if(parts->count < 2)
{
jdns_stringlist_delete(parts);
continue;
}
simp = string_tolower(parts->item[0]);
if(strcmp((char *)simp->data, "nameserver") == 0)
{
jdns_address_t *addr = jdns_address_new();
jdns_address_set_cstr(addr, (const char *)parts->item[1]->data);
jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
jdns_address_delete(addr);
}
else if(strcmp((char *)simp->data, "search") == 0)
{
for(n = 1; n < parts->count; ++n)
{
jdns_dnsparams_append_domain(params, parts->item[n]);
}
}
else if(strcmp((char *)simp->data, "domain") == 0)
{
jdns_dnsparams_append_domain(params, parts->item[1]);
}
jdns_string_delete(simp);
jdns_stringlist_delete(parts);
}
fclose(f);
return params;
}
#if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3)))
# define JDNS_MODERN_RES_API
#endif
#ifndef JDNS_MODERN_RES_API
typedef int (*res_init_func)();
static int my_res_init()
{
#ifdef JDNS_OS_MAC
res_init_func mac_res_init;
// look up res_init in the system library (qt does this, not sure why)
mac_res_init = (res_init_func)dlsym(RTLD_NEXT, "res_init");
if(!mac_res_init)
return -1;
return mac_res_init();
#else
return res_init();
#endif
}
#endif
#ifdef __res_state_ext
# define USE_EXTEXT
#endif
static jdns_dnsparams_t *dnsparams_get_unixsys()
{
int n;
jdns_dnsparams_t *params;
#ifdef JDNS_MODERN_RES_API
struct __res_state res;
memset(&res, 0, sizeof(struct __res_state));
n = res_ninit(&res);
#define RESVAR res
#else
n = my_res_init();
#define RESVAR _res
#endif
params = jdns_dnsparams_new();
// error initializing?
if(n == -1)
return params;
// nameservers - ipv6
for(n = 0; n < MAXNS && n < RESVAR._u._ext.nscount; ++n)
{
jdns_address_t *addr;
struct sockaddr_in6 *sa6;
#ifdef USE_EXTEXT
sa6 = ((struct sockaddr_in6 *)RESVAR._u._ext.ext) + n;
#else
sa6 = RESVAR._u._ext.nsaddrs[n];
#endif
if(sa6 == NULL)
continue;
addr = jdns_address_new();
jdns_address_set_ipv6(addr, sa6->sin6_addr.s6_addr);
jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
jdns_address_delete(addr);
}
// nameservers - ipv4
for(n = 0; n < MAXNS && n < RESVAR.nscount; ++n)
{
jdns_address_t *addr = jdns_address_new();
jdns_address_set_ipv4(addr, ntohl(RESVAR.nsaddr_list[n].sin_addr.s_addr));
jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
jdns_address_delete(addr);
}
// domain name
if(strlen(RESVAR.defdname) > 0)
{
jdns_string_t *str;
jdns_string_t *p;
str = jdns_string_new();
jdns_string_set_cstr(str, RESVAR.defdname);
p = string_tolower(str);
jdns_string_delete(str);
str = p;
jdns_dnsparams_append_domain(params, str);
jdns_string_delete(str);
}
// search list
#ifdef MAXDFLSRCH
for(n = 0; n < MAXDFLSRCH && RESVAR.dnsrch[n]; ++n)
{
if(strlen(RESVAR.dnsrch[n]) > 0)
{
jdns_string_t *str;
jdns_string_t *p;
str = jdns_string_new();
jdns_string_set_cstr(str, RESVAR.dnsrch[n]);
p = string_tolower(str);
jdns_string_delete(str);
str = p;
// don't add dups
if(!dnsparams_have_domain(params, str))
jdns_dnsparams_append_domain(params, str);
jdns_string_delete(str);
}
}
#endif
return params;
}
static jdns_dnsparams_t *dnsparams_get_unix()
{
jdns_dnsparams_t *params;
// prefer system calls over files
params = dnsparams_get_unixsys();
if(params->nameservers->count == 0)
{
jdns_dnsparams_delete(params);
params = dnsparams_get_unixfiles();
}
apply_hosts_file(params, "/etc/hosts");
return params;
}
#endif
jdns_dnsparams_t *jdns_system_dnsparams()
{
#ifdef JDNS_OS_WIN
return dnsparams_get_win();
#else
return dnsparams_get_unix();
#endif
}