It's no wonder why pages take so long to load if each one of these things takes up to 1s. Simply switching my DNS server from my ISP's to Google's brought the time down from 500+ms to ~25ms, but that is still 1s (not including the download time) for a page with 80 additional resources. Most browsers will use the OS builtin caching for repeated access to the same host, but Linux (glibc) does not have it (android does, but it is buggy) There are also daemons like nscd that are also buggy.
Checking glibc, musl and uclibc - none of their libresolv (or libc if it is integrated) have any builtin caching, so it is up to us to provide it. The DNS caching alone will make a lot of improvement, but the ip addresses come in no particular order, so it would be good to sort them by response time (in cases like CDNs that have many IP addresses that may be on the other side of the internet) These 2 little things could provide significant improvement in user experience for desktop users.
But enough B.S. I'll shut up and show you the code.
Code: Select all
#include <netinet/in.h>
#include <fcntl.h>
#include <string.h>
int get_value(const char *path, void *buf,size_t len){
int fd = open(path, O_RDONLY);
if (fd<0) return fd;
len=read(fd,buf,len);
close(fd);
return len;
}
int set_value(const char *path, void *buf,size_t len){
int fd = open(path, O_CREAT|O_WRONLY|O_TRUNC);
if (fd<0) return fd;
len=write(fd,buf,len);
close(fd);
return len;
}
uint32_t query_ip(char *host, uint32_t dns){
unsigned char buf[4096]={0}, *bufp=buf,
*hp=(unsigned char *)"\0\0" "\x01\0" "\0\x01" "\0\0" "\0\0" "\0\0";
struct sockaddr_in dest = { //CN 0x72727272 RU 0x3E4C4C3E US2 0x08080808
.sin_family=AF_INET, .sin_port=htons(53), .sin_addr.s_addr=dns
};
uint32_t i, j, ans, ip=0, destsz=sizeof(struct sockaddr_in);
int s=socket(AF_INET , SOCK_DGRAM , IPPROTO_UDP);
if (s<0) goto IPV4END;
for(i=0;i<12;i++) *bufp++=*hp++; //copy header
i=j=0;
do{ /* convert www.example.com to 3www7example3com */
if(host[i]=='.' || !host[i]){ //could use strchrnul() here instead
*bufp++ = i-j;
for(;j<i;j++)
*bufp++=host[j];
++j;
}
}while(host[i++]);
*bufp++='\0';
if (!(bufp-buf)&1) *bufp++='\0'; //pad my shorts... is it sexxy?
*(bufp++)=0; *(bufp++)=1; *(bufp++)=0; *(bufp++)=1; //extra Q fields
i=sendto(s, buf, bufp-buf, 0, (struct sockaddr*)&dest, destsz);
if (i < 0) goto IPV4END;
i=recvfrom(s,buf,sizeof(buf),0,(struct sockaddr*)&dest,(socklen_t*)&destsz);
if (i < 0) goto IPV4END;
for(i=0;i<buf[7];i++){ //[7] holds num of answers([6] does too but >256?)
while(*bufp) ++bufp; //skip names
ans=bufp[1]; //[1] holds the answer type ([0] does too, but >256???)
bufp += 10;
if(ans == 1){ uint32_t j=4; // ipv4 address
unsigned char *ipp=(unsigned char *)&ip;
while(j--) *ipp++=*bufp++;
goto IPV4END; //temporary hack ... todo write all of them to cache
}else while(*bufp) ++bufp; //todo read these and make hard links
}
IPV4END:
close(s);
return ip;
}
//This is a wrapper around query_ip, and most of it should eventually move there.
//TODO app to select optimal DNS server and generate the /*/.hosts/dns
//TODO actually read alias host names and make them into hardlinks
//TODO set the TTL by adding it to current time and changing the files mod time.
// ... thus if current time > modified time, do another query
// ... only add a short period of time to "not found" in case its offline
//TODO write separate app/daemon to sort the IP entries from fastest to slowest
// ... a daemon could use an inotify watch on the */.hosts/ directory
uint32_t host2ip(char *host){
char path[254+sizeof("/etc/.hosts/")]; //using /etc for now -> /tmp ???
uint32_t dns[2]={0x04020204, 0x08080808}, ip=0;
int res;
strcpy(path,"/etc/.hosts/");
if (mkdir(path) == 0){
uint32_t dns[] = {0x04020204, 0x08080808,0};
res = set_value("/etc/.hosts/" "dns", &dns, sizeof(dns));
}else res = get_value("/etc/.hosts/" "dns", &dns, sizeof(dns));
strncat(path,host,sizeof(path));
if (get_value(path,&ip,sizeof(ip)) < 0){
if((ip=query_ip(host,dns[0])) || (ip=query_ip(host,dns[1])))
set_value(path,&ip,sizeof(ip));
}
return ip;
}
#ifdef TEST
#include <stdio.h> //printf ... adds ~16k on static musl builds
int main( int argc ,char **argv){
if (argc < 2) return 1;
in_addr_t ip=host2ip(argv[1]);
if (!ip){
perror("host2ip");
return 1;
}
printf("%d.%d.%d.%d\n",((unsigned char*)&ip)[0],((unsigned char*)&ip)[1],((unsigned char*)&ip)[2],((unsigned char*)&ip)[3]);
return 0;
}
#endif
format:
"/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>"
see:
security/keys and net/dns_resolver and keyutils
This is now plausible after this commit for 3.18:
https://git.kernel.org/cgit/linux/kerne ... 03a0029e38
Edit 2: After some consideration, I came to the conclusion that using a filesytem based cache is just as efficient, easier to implemnetn and more portable than using kernel interaction.