Subject: Re: sethostent(1): is it really that useful with a DNS?
To: Andrew Brown <atatat@atatdot.net>
From: Chuck Cranor <chuck@research.att.com>
List: tech-userlevel
Date: 06/16/2001 21:55:34
On Mon, Jun 11, 2001 at 11:49:23AM -0400, Andrew Brown wrote:
> >what is this sethostent(1) really buying us?    the man page says:

> i imagine you get slightly more robust performance from the dns via
> tcp than via udp.  combine that with the fact that netstat (when not
> using -n) will be doing a lot of lookups, and it probably *is* useful,
> even if slightly strange looking to the uninitiated.

in the typical config (local DNS "close" to resolver client on network)
i don't think you'll get any real performance boost from TCP.   e.g. 
consider NFS which uses UDP in most cases like this.  in the file-based 
/etc/hosts world, sethostent(1) makes some sense because you can save
the close and reopen of the file for each lookup.  but i don't think this
gain translates for using TCP over UDP for DNS requests.

i would contend that sethostent(1) should just keep the UDP client 
socket around, but it should not switch to TCP.   (i.e. set
RES_STAYOPEN but don't set RES_USEVC.)

 
> on the other hand, if netstat was using udp...might it not be
> continually looking up the names of the addresses of the udp sockets
> it was continually opening up to look up the names of the addresses of
> the udp socket that it just used to look up the last names...

i'm not sure i understand what you are saying.


to get more detail i decided to ktrace a simple program that does:
	sethostent(x);		/* x=0 or x=1 */
	gethostbyname(host1);
	gethostbyname(host2);


					
x=0 case (sethostent(0))		x=1 case (sethostent(1))

	[sethostent(0) results in the following syscalls]
gettimeofday();				<<< SAME as x=0 case >>>
getpid();
open("/etc/resolv.conf") = 3
__fstat13(3)
__sysctl(...)
readlink("/etc/malloc.conf");
mmap(0,0x1000,0x3,0x1002,
	0xffffffff,0,0,0)
break(...) [5 times]
read(3,..) // resolv.conf
break(...) [2 times]
read(3,..) // resolv.conf EOF	
close(3) 


	[gethostbyname(host1) results in the following syscalls]
__stat13("/etc/nsswitch.conf")		__stat13("/etc/nsswitch.conf")
open("/etc/nsswitch.conf",..)=3		open("/etc/nsswitch.conf",..)=3
break() [2 times]			break() [2 times]
ioctl(0x3, TIOCGETA,...)		ioctl(0x3, TIOCGETA,...)
__fstat13(3,...)			__fstat13(3,...)
break() [2 times]			break() [2 times]
read(3,...) // nsswitch.conf 		read(3,...) // nsswitch.conf
read(3,...) // nsswitch EOF 		read(3,...) // nsswitch EOF
break() [4 times]			break() [4 times]
ioctl(0x3, TIOCGETA,...) // AGAIN?	ioctl(0x3, TIOCGETA,...) // AGAIN?
close(3)				close(3)
open("/etc/hosts",...) = 3		open("/etc/hosts",...) = 3
__fstat13(3,...)			__fstat13(3,...)
read(3,...) // /etc/hosts		read(3,...) // /etc/hosts
read(3,...) // EOF hosts		read(3,...) // EOF hosts
close(3)				close(3) // XXX: WRONG? stayopen=1
/*			we diverge here 				*/
socket(2,2,0) = 3 // UDP sock		socket(2,1,0) = 3 // TCP sock
connect(3,...) // UDP connect		connect(3,...)  // TCP connect
sendto(3,...)  // send req		writev(3,...)   // send req
gettimeofday()				read(3,..,2)  // read first 2 bytes
poll()					read(3,...) // rest rest of reply
recvfrom(3,...) // read answer		/*DONE*/
close(3)
/*DONE*/


	[gethostbyname(host2) results in the following syscalls]
__stat13("/etc/nsswitch.conf", ...)	__stat13("/etc/nsswitch.conf", ...) 
	/* libc decides cached value of nsswitch.conf is ok? */
open("/etc/hosts",...) = 3		open("/etc/hosts",...) = 4
__fstat13(3,...)			__fstat13(4,...)
read(3,...) // /etc/hosts		read(4,...) // /etc/hosts
read(3,...) // EOF hosts		read(4,...) // EOF hosts
close(3)				close(4) // XXX: WRONG? stayopen=1
socket(2,2,0) = 3 // UDP socket		writev(3,...) // reuse TCP,send req
connect(3,...) // UDP connect		read(3,..,2) // read 2 bytes of reply
sendto(3,...)  // send req		read(3,...) // read rest of reply
gettimeofday()				/*DONE*/
poll()
recvfrom(3,...)// read answer
close(3)
/*DONE*/


if you change sethostent(1) from (RES_STAYOPEN|RES_USEVC) to 
just RES_STAYOPEN as i suggest, then the UDP socket is not closed
and the gethostbyname(host2) trace becomes:
  __stat13("/etc/nsswitch.conf", ...) 
  open("/etc/hosts",...) = 4
  __fstat13(4,...)
  read(4,...) // /etc/hosts
  read(4,...) // EOF hosts
  close(4)    // XXX: WRONG? stayopen=1
  sendto(3,...)  // sed req
  gettimeofday()
  poll()
  recvfrom(3,..)  // read answer

which is one more syscall than the TCP case...   it might also
be more robust in the case where the remote DNS hangs as the timeout
mechanism doesn't seem to be used in TCP (i.e. the program could hang
in the 2-byte read after the writev in the TCP case --- the UDP case
uses poll with a timeout).


so to sum up:
 1. i think sethostent(stayopen=1) should do RES_STAYOPEN rather than 
		(RES_STAYOPEN|RES_USEVC)

 2. there seems to be some un-necessary ioctl(0x3, TIOCGETA,...)
	calls in the code path

 3. it seems that sethostent(stayopen=1) should keep /etc/hosts open
	if it is using it.   but it closes it anyway.
 4. resolver(3) documents RES_STAYOPEN like this:

	Used with RES_USEVC to keep the TCP connection open be-
	tween queries.  This is useful only in programs that regu-
	larly do many queries.  UDP should be the normal mode
	used.

    i read this to say that RES_STAYOPEN is only used with TCP (RES_USEVC).
    a system call trace shows that it works with UDP too (saves a couple
    of syscalls).


chuck