[net.bugs.4bsd] WARNING about 4.2BSD connect

dae@psuvax1.UUCP (Daemon) (01/01/85)

Description:
    A connect() call that fails due to a missing recipient
    invalidates later uses of the socket:

    s = socket(stuff, stuff, stuff);
    connect (s, stuff, stuff);
    /* let's say it fails because recipient socket isn't there */

    connect (s, stuff, stuff);
      or
    listen(s, anything)
    /* fails, errno =  EINVAL */

    In other words, if a connect() fails, the socket must
    be closed and totally regenerated.
    It could be argued that this is not a bug, since at least
    the listen on a connected socket should fail, BUT

    connect(s, stuff, stuff) /* let's say it succeeds */
    listen(s, anything) /* succeeds !!! */

Repeat-by:
    The following code is really sad, but it
    serves to illustrate the point:

    a.out hostname servername

    try servers that don't exist, and one server
    that does exist (in /etc/services) but is not
    currently active.  Instructive, no?

#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>

extern int errno;

main(argc, argv)
int argc;
char **argv;
{
    struct hostent *hp, *gethostbyname();
    struct servent *sp, *getservbyname();
    struct protoent *pp, *getprotobyname();
    struct sockaddr_in sin;
    int s;

    if (argc != 3)
        exit(-5);

    bzero(&sin, sizeof(sin));

    if ( (sp = getservbyname(argv[2], (char *) 0)) == (struct servent *) 0)
        die("serv");
    
    sin.sin_port = sp->s_port;
    sin.sin_family = AF_INET;

    if ( (hp = gethostbyname(argv[1])) == (struct hostent *) 0)
        die ("host");

    bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);

    if ( (pp = getprotobyname(sp->s_proto)) == (struct protoent *) 0)
        die("proto");
    
    if ( (s = socket(hp->h_addrtype, SOCK_STREAM, pp->p_proto)) == -1)
        die("socket");
    
    if ( connect(s, &sin, sizeof(sin)) == -1 )
        {
        perror("connect (first time)");
        if ( connect(s, &sin, sizeof(sin)) == -1 )
            perror("connect (second time)");
        else
            printf("no connect error (second time)\n");
        }
    else
        printf("no connect error (first time)\n");

    if (listen(s, 1) == -1)
        perror("listen");
    else
        printf("no listen error\n");

    if (close(s) == -1)
        perror("close");
    else
        printf("no close error\n");

    exit(0);
}
int
die(m)
char *m;
{
    printf("Death = %s\nErrno = %d\n", m, errno);
    perror("die");
    exit(-45);
}

Also-in:
    /usr/src/usr.bin/lpr/common.c, function getport()
    re-makes the socket every time the connect fails.

Fix:
    I have absolutely no idea.  Sorry, folks, but I'm
    not really a wizard.  Just thought I'd warn you
    that it's not your code that's failing, but
    the kernel.

Complaint:
    Anybody ever noticed how hard it is to have two *peer*
    processes get a socket 'tween them?  Berkeley (or tcp) is
    really into this client-server (masochist-sadist)
    philosophy--one person has to grovel, and the other may deign to
    accept conversation.  The lpr code (which I wish I had seen
	*before* I wrote mine) should be very instructive.

Questions:
    Is the ambiguity of ECONNREFUSED (can mean (1) nobody there
    or (2) his queue is zero-length) inherent to tcp?

    Is anybody writing any other protocols out there?  please?

-- 

 
 \ / \/
  \  / From the furnace of Daemon ( ...{psuvax1,gondor,shire}!dae )
   \/  (814) 237-1901 "I will have no covenants but proximities" [Emerson]

When the going gets tough, the weird turn pro.