From mikko@dynas.se  Wed Sep 27 13:13:55 2000
Return-Path: <mikko@dynas.se>
Received: from karon.dynas.se (karon.dynas.se [192.71.43.4])
	by hub.freebsd.org (Postfix) with SMTP id DB0C837B422
	for <FreeBSD-gnats-submit@freebsd.org>; Wed, 27 Sep 2000 13:13:53 -0700 (PDT)
Received: (qmail 89815 invoked from network); 27 Sep 2000 20:13:51 -0000
Received: from spirit.sto.dynas.se (HELO spirit.dynas.se) (172.16.1.10)
  by karon.sto.dynas.se with SMTP; 27 Sep 2000 20:13:51 -0000
Received: (qmail 10194 invoked from network); 27 Sep 2000 20:14:00 -0000
Received: from explorer.rsa.com (10.81.217.59)
  by spirit.dynas.se with SMTP; 27 Sep 2000 20:14:00 -0000
Received: (from mikko@localhost)
	by explorer.rsa.com (8.11.0/8.11.0) id e8RKDjD11153;
	Wed, 27 Sep 2000 13:13:45 -0700 (PDT)
	(envelope-from mikko)
Message-Id: <200009272013.e8RKDjD11153@explorer.rsa.com>
Date: Wed, 27 Sep 2000 13:13:45 -0700 (PDT)
From: mikko@dynas.se
Reply-To: mikko@dynas.se
To: FreeBSD-gnats-submit@freebsd.org
Subject: kevent() does not detect UDP ECONNREFUSED
X-Send-Pr-Version: 3.2

>Number:         21601
>Category:       kern
>Synopsis:       kevent() does not detect UDP ECONNREFUSED
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Sep 27 13:20:00 PDT 2000
>Closed-Date:    Wed Sep 27 21:53:12 PDT 2000
>Last-Modified:  Wed Sep 27 21:53:38 PDT 2000
>Originator:     Mikko Tyolajarvi
>Release:        FreeBSD 4.1-STABLE i386
>Organization:
>Environment:

This is on -STABLE as of a few days ago (Sep 24, 2000).  It is
probably in 4.1.1R

>Description:

Since the change from poll() to kqueue() in the resolver, res_send()
is no longer able to "detect the absence of a name server without
timing out" (quoted from res_send.c).

Unless there is some (undocumented) flag combination I haven't found
(and which is not used res_send), kevent() does not detect the socket
error that you get when sending an UDP datagram from a connected
socket to a port where nobody is listening.  So, I assume this is a
kernel bug.

[ For me this manifests itself as all sorts of network related
  operations being really slow on my laptop, which is the victim of a
  braindead DHCP setup, listing 127.0.0.1 as the first name server,
  and as bin/15046 continues to be ignored, the hook provided in
  /sbin/dhclient-script, which would have offered a simple, documented
  way to filter the data is not available >:-( ]

Another way to put it is "traceroute cannot be rewritten to use kqueue()"


>How-To-Repeat:

The following little program will send an UDP datagram to a port,
using either select() or kevent() to wait for error indication.

Thus (on a machine with no name server, but any unused port will do):

 % ./a.out 127.0.0.1 53			# Uses select()
 socket error: Connection refused
 % ./a.out 127.0.0.1 53 foo		# Uses kevent()
 timeout (1 second)

---8<----------------------------------------------------------------------
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/event.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>

/*
 * Usage: ./a.out dotted-ip port-num [ use-kqueue ]
 */

int
main(int argc, char **argv)
{
    struct sockaddr_in ia;
    struct kevent kv;
    int sock, n, err;

    ia.sin_family = AF_INET;
    ia.sin_addr.s_addr = inet_addr(argv[1]);
    ia.sin_port = htons(atoi(argv[2]));

    sock = socket(PF_INET, SOCK_DGRAM, 0);
    if (connect(sock, (struct sockaddr *)&ia, sizeof(ia)) < 0) {
	perror("connect");
	return 1;
    }
    if (send(sock, "?", 1, 0) != 1) {
	perror("send");
	return 1;
    }

    if (argc > 3) {
	struct timespec ts;
	int kq;

	kq = kqueue();

	ts.tv_sec = 1;
	ts.tv_nsec = 0;

	memset(&kv, 0, sizeof(kv));
	kv.ident = sock;
	kv.flags = EV_ADD | EV_ONESHOT;
	kv.filter = EVFILT_READ;
	
	n = kevent(kq, &kv, 1, &kv, 1, &ts);
    } else {
	struct timeval tv;
	fd_set fds;

	FD_ZERO(&fds);
	FD_SET(sock, &fds);

	tv.tv_sec = 1;
	tv.tv_usec = 0;

	n = select(sock+1, &fds, NULL, NULL, &tv);
    }

    switch (n) {
      case 0:
	printf("timeout (one second)\n");
	break;

      case 1:
	n = sizeof(err);
	if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &n) < 0) {
	    perror("getsockopt");
	} else {
	    printf("socket error: %s\n", strerror(err));
	}
	break;

      default:
	perror("kevent/select\n");
	break;
    }
    return 0;
}
---8<----------------------------------------------------------------------

>Fix:
>Release-Note:
>Audit-Trail:
State-Changed-From-To: open->closed 
State-Changed-By: jlemon 
State-Changed-When: Wed Sep 27 21:53:12 PDT 2000 
State-Changed-Why:  
Bug found, fix applied to -stable and -current. 


http://www.freebsd.org/cgi/query-pr.cgi?pr=21601 
>Unformatted:
