Net2/usr/src/contrib/isode/others/X/server/connection.c

Compare this file to the similar file:
Show the results in this format:

/***********************************************************
Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
and the Massachusetts Institute of Technology, Cambridge, Massachusetts.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the names of Digital or MIT not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

******************************************************************/
/* $XConsortium: connection.c,v 1.88 88/10/22 22:06:31 keith Exp $ */
/*****************************************************************
 *  Stuff to create connections --- OS dependent
 *
 *      EstablishNewConnections, CreateWellKnownSockets, ResetWellKnownSockets,
 *      CloseDownConnection, CheckConnections, AddEnabledDevice,
 *	RemoveEnabledDevice, OnlyListToOneClient,
 *      ListenToAllClients,
 *
 *      (WaitForSomething is in its own file)
 *
 *      In this implementation, a client socket table is not kept.
 *      Instead, what would be the index into the table is just the
 *      file descriptor of the socket.  This won't work for if the
 *      socket ids aren't small nums (0 - 2^8)
 *
 *****************************************************************/

#ifdef ISOCONN
#include <math.h>
#endif /* ISOCONN */

#include <dbm.h>
#undef NULL
#include "X.h"
#include "Xproto.h"
#include <sys/param.h>
#include <errno.h>
#include "Xos.h"			/* for strings, file, time */
#include <sys/socket.h>

#include <signal.h>
#include <fcntl.h>
#include <setjmp.h>

#ifdef hpux
#include <sys/ioctl.h>
#endif

#ifdef TCPCONN
#include <netinet/in.h>
#ifndef hpux
#include <netinet/tcp.h>
#endif
#endif

#ifdef UNIXCONN
/*
 * sites should be careful to have separate /tmp directories for diskless nodes
 */
#include <sys/un.h>
#include <sys/stat.h>
static int unixDomainConnection = -1;
#endif

#include <stdio.h>
#include <sys/uio.h>
#include "osstruct.h"
#include "osdep.h"
#include "opaque.h"
#include "dixstruct.h"

#ifdef DNETCONN
#include <netdnet/dn.h>
#endif /* DNETCONN */

#ifdef ISOCONN
#include <isode/psap.h>
#include <isode/tsap.h>
#include <isode/isoservent.h>

extern char *isodetcpath; 

#ifdef ISODEBUG
/*
 * Set to true for loads of TSAP messages...
 */
int isodexbug = FALSE;
#endif /* ISODEBUG */

/*
 * array of fd 2 family map so we can lookup right function below...
 * Its initialised at connection setup...
 */
int fd2family[MAXSOCKS];
/*
 * Globals for storing functions appropos each fd/socket type
 * UNIX_IO (0) map to sys calls
 * ISODE_IO (1) maps to my fns...
 */

extern int accept(), TAcceptFromClient();
int (*acceptfn[])() =
{
	accept, TAcceptFromClient
};
extern getpeername(), getISOpeername();
int (*getpeerfn[])() =
{
	getpeername, getISOpeername
};
/*
 * Note yuckiness here XXX
 * TReadFromClient takes one more param than read - must fix...
 */
extern int read(), TReadFromClient();
int (*readfn[])() = 
{
	read, TReadFromClient
};
extern int write(), TWriteToClient();
int (*writefn[])() =
{
	write, TWriteToClient
};
extern writev(), TWritevToClient();
int (*writevfn[])() =
{
	writev, TWritevToClient
};
extern int close(), TDiscFromClient();
int (*closefn[])() =
{
	close, TDiscFromClient
};

#endif /* ISOCONN */

typedef long CCID;      /* mask of indices into client socket table */

#ifndef X_UNIX_PATH
#define X_UNIX_DIR	"/tmp/.X11-unix"
#define X_UNIX_PATH	"/tmp/.X11-unix/X"
#endif

#ifdef ISOCONN
char *display = NULLCP;			/* The display number */
#else /* ISOCONN */
char *display;			/* The display number */
#endif /* ISOCONN */
int lastfdesc;			/* maximum file descriptor */

long WellKnownConnections;	/* Listener mask */
long EnabledDevices;		/* mask for input devices that are on */
long AllSockets[mskcnt];	/* select on this */
long AllClients[mskcnt];	/* available clients */
long LastSelectMask[mskcnt];	/* mask returned from last select call */
long ClientsWithInput[mskcnt];	/* clients with FULL requests in buffer */
long ClientsWriteBlocked[mskcnt];/* clients who cannot receive output */
long OutputPending[mskcnt];	/* clients with reply/event data ready to go */
long MaxClients = MAXSOCKS ;
long OutputBufferSize = BUFSIZ; /* output buffer size (must be > 0) */
long NConnBitArrays = mskcnt;
long FirstClient;
Bool NewOutputPending;		/* not yet attempted to write some new output */
Bool AnyClientsWriteBlocked;	/* true if some client blocked on write */

static Bool debug_conns = FALSE;

static char whichByteIsFirst;

static int SavedAllClients[mskcnt];
static int SavedAllSockets[mskcnt];
static int SavedClientsWithInput[mskcnt];
static Bool GrabDone = FALSE;

ClientPtr ConnectionTranslation[MAXSOCKS];
extern ClientPtr NextAvailableClient();

extern ConnectionInput inputBuffers[];

int swappedClients[MAXSOCKS];

extern int AutoResetServer();
extern int GiveUp();

#ifdef UNIXCONN

static struct sockaddr_un unsock;

static int open_unix_socket ()
{
    int oldUmask;
    int request;

    unsock.sun_family = AF_UNIX;
    oldUmask = umask (0);
#ifdef X_UNIX_DIR
    mkdir (X_UNIX_DIR, 0777);
#endif
    strcpy (unsock.sun_path, X_UNIX_PATH);
    strcat (unsock.sun_path, display);
    unlink (unsock.sun_path);
    if ((request = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) 
    {
	Notice ("Creating Unix socket");
    } 
    else 
    {
	if(bind(request,(struct sockaddr *)&unsock, strlen(unsock.sun_path)+2))
	    Error ("Binding Unix socket");
	if (listen (request, 5)) Error ("Unix Listening");
    }
    (void)umask(oldUmask);
    return request;
}
#endif /*UNIXCONN */

/*****************
 * CreateWellKnownSockets
 *    At initialization, create the sockets to listen on for new clients.
 *    There are potentially 4: DECnet, UNIX Domain, TCP-IP with MSB first, 
 *    with TCP-IP with LSB first.
 *****************/

void
CreateWellKnownSockets()
{
    int		request, i;
    int		whichbyte;	    /* used to figure out whether this is
   					 LSB or MSB */
#ifdef TCPCONN
    struct sockaddr_in insock;
    int		tcpportReg;	    /* port with same byte order as server */

#ifdef SO_LINGER
    static int linger[2] = { 0, 0 };
#endif /* SO_LINGER */

#endif /* TCPCONN */

#ifdef DNETCONN
    struct sockaddr_dn dnsock;
#endif /* DNETCONN */

#ifdef ISOCONN
    struct TSAPdisconnect tds;
    struct TSAPdisconnect *td = &tds;
    struct TSAPaddr tas;
    struct TSAPaddr *ta = &tas;
    struct PSAPaddr *pa;
    AEI   aei;
#endif /* ISOCONN */
    int retry;

#ifdef ISOCONN
#ifdef ISODEBUG
    isodetcpath = ISODEPATH;
#endif
#endif /* ISOCONN */

    CLEARBITS(AllSockets);
    CLEARBITS(AllClients);
    CLEARBITS(LastSelectMask);
    CLEARBITS(ClientsWithInput);

    for (i=0; i<MAXSOCKS; i++) ConnectionTranslation[i] = (ClientPtr)NULL;
    
#ifdef	hpux
	lastfdesc = _NFILE - 1;
#else
	lastfdesc = getdtablesize() - 1;
#endif	/* hpux */

    if (lastfdesc > MAXSOCKS)
    {
	lastfdesc = MAXSOCKS;
	if (debug_conns)
	    ErrorF( "GOT TO END OF SOCKETS %d\n", MAXSOCKS);
    }

    WellKnownConnections = 0;
    whichbyte = 1;
    
    if (*(char *) &whichbyte)
        whichByteIsFirst = 'l';
    else
        whichByteIsFirst = 'B';


#ifdef TCPCONN

    tcpportReg = atoi (display); 
    tcpportReg += X_TCP_PORT;

    if ((request = socket (AF_INET, SOCK_STREAM, 0)) < 0) 
    {
	Notice ("Creating TCP socket");
    } 
    else 
    {
	bzero ((char *)&insock, sizeof (insock));
	insock.sin_family = AF_INET;
	insock.sin_port = htons (tcpportReg);
	insock.sin_addr.s_addr = htonl(INADDR_ANY);
	retry = 20;
	while (i = bind(request, (struct sockaddr *) &insock, sizeof (insock))) 
	{
#ifdef hpux
	    /* Necesary to restart the server without a reboot */
	    if (errno == EADDRINUSE)
		set_socket_option (request, SO_REUSEADDR);
	    if (--retry == 0)
		Error ("Binding TCP socket");
	    sleep (1);
#else
	    if (--retry == 0)
		Error ("Binding MSB TCP socket");
	    sleep (10);
#endif /* hpux */
	}
#ifdef hpux
	/* return the socket option to the original */
	if (errno)
	    unset_socket_option (request, SO_REUSEADDR);
#endif /* hpux */
#ifdef SO_LINGER
	if(setsockopt (request, SOL_SOCKET, SO_LINGER,
		       (char *)linger, sizeof(linger)))
	    Notice ("Setting TCP SO_LINGER\n");
#endif /* SO_LINGER */
	if (listen (request, 5))
	    Error ("Reg TCP Listening");
	WellKnownConnections |= (1 << request);
	DefineSelf (request);
#ifdef ISOCONN
	fd2family[request] = UNIX_IO;
#endif /* ISOCONN */
    }

#endif /* TCPCONN */

#ifdef UNIXCONN
    if ((request = open_unix_socket ()) != -1) {
	WellKnownConnections |= (1L << request);
	unixDomainConnection = request;
#ifdef ISOCONN
    fd2family[request] = UNIX_IO;
#endif /* ISOCONN */
    }
#endif /*UNIXCONN */

#ifdef DNETCONN
    if ((request = socket (AF_DECnet, SOCK_STREAM, 0)) < 0) 
    {
	Notice ("Creating DECnet socket");
    } 
    else 
    {
	bzero ((char *)&dnsock, sizeof (dnsock));
	dnsock.sdn_family = AF_DECnet;
	sprintf(dnsock.sdn_objname, "X$X%d", atoi (display));
	dnsock.sdn_objnamel = strlen(dnsock.sdn_objname);
	if (bind (request, (struct sockaddr *) &dnsock, sizeof (dnsock)))
		Error ("Binding DECnet socket");
	if (listen (request, 5)) Error ("DECnet Listening");
	WellKnownConnections |= (1 << request);
	DefineSelf (request);
#ifdef ISOCONN
    fd2family[request] = UNIX_IO;
#endif /* ISOCONN */
    }
#endif /* DNETCONN */
#ifdef ISOCONN
/*
 * If display is set, its the string after the Colon:
 * i.e. X0 or X1 or T0 or T1...
 */
    if ((display == NULLCP) || (atoi(display) == 0))
	aei = str2aei(TLocalHostName(), DEFAULTTSERVICE);
    else
	aei = str2aei(TLocalHostName(), display);

    if (aei == NULLAEI) {
	ErrorF("No AEI for me:");
	FatalError(TLocalHostName());
    }

/*
 * This hack only works if the PSAPaddr and SSAP addrsd are null!!
 */
     if ((pa = aei2addr (aei)) == NULLPA) 
	FatalError("address translation failed");

    ta = (struct TSAPaddr *)&(pa->pa_addr.sa_addr);

/*
 * Just put out a listen for now
 */
    if ((request = TNetListen(ta, td)) != OK) {
	Error(TErrString(td->td_reason));
	FatalError("TNetListen");
    }

    WellKnownConnections |= (1 << request);
    DefineSelf (request);
    fd2family[request] = ISODE_IO;
#endif /*  ISOCONN */
    if (WellKnownConnections == 0)
        Error ("No Listeners, nothing to do");
    signal (SIGPIPE, SIG_IGN);
    signal (SIGHUP, AutoResetServer);
    signal (SIGINT, GiveUp);
    signal (SIGTERM, GiveUp);
    FirstClient = request + 1;
    AllSockets[0] = WellKnownConnections;
    ResetHosts(display);

    for (i=0; i<MaxClients; i++)
    {
	inputBuffers[i].buffer = (char *) NULL;
	inputBuffers[i].bufptr = (char *) NULL;
	inputBuffers[i].bufcnt = 0;
	inputBuffers[i].lenLastReq = 0;
	inputBuffers[i].size = 0;
    }
}

void
ResetWellKnownSockets ()
{
#ifdef UNIXCONN
    if (unixDomainConnection != -1)
    {
	/*
	 * see if the unix domain socket has disappeared
	 */
	struct stat	statb;

	if (stat (unsock.sun_path, &statb) == -1 ||
	    (statb.st_mode & S_IFMT) != S_IFSOCK)
	{
	    ErrorF ("Unix domain socket %s trashed, recreating\n",
	    	unsock.sun_path);
	    (void) unlink (unsock.sun_path);
	    (void) close (unixDomainConnection);
	    WellKnownConnections &= ~(1L << unixDomainConnection);
	    unixDomainConnection = open_unix_socket ();
	    if (unixDomainConnection != -1)
		WellKnownConnections |= (1L << unixDomainConnection);
	}
    }
#endif /* UNIXCONN */
}

#ifdef ISOCONN
/*
 * Convenience routine...
 * client = transport descriptor
 * data, size = buffer
 * nonblock = NOTOK, blocks, OK, non blocks
 */
TReadFromClient(client, data, size, nonblock)
int client, size, nonblock;
char *data;
{
	struct TSAPdisconnect tds;
	struct TSAPdisconnect *td = &tds;
	int ret;
static struct TSAPdata txs;
static struct TSAPdata *tx = &txs;
	char *aptr; 
static struct qbuf *qb;
static char *qptr;
static int ingot, qcpy, result = 0;
	int q2data;

#ifdef ISODEBUG
	if (isodexbug) {
		fprintf(stderr, "TReadFromClient %d want %d (%d buffered)\n", 
			client, size, result);
	}
#endif /* ISODEBUG */
	if (result == 0) {
		if ((ret = TReadRequest(client, tx, nonblock, td)) == NOTOK) {
#ifdef ISODEBUG
		    if(errno == EWOULDBLOCK ) {
			fprintf(stderr, "Server TReadReq: would block %s\n",
				TErrString(td->td_reason));
			if (!DR_FATAL(td->td_reason))
				errno = EWOULDBLOCK;
			else
				errno = EBADF;
			return ret;
		    } 
		    if (isodexbug)
			    fprintf(stderr, "Server TReadReq: %s\n", 
				TErrString(td->td_reason));
#endif /* ISODEBUG */
/*
 * map problems here - eg TimeOut...
 */
		    if (td->td_reason == DR_TIMER)
			errno = EWOULDBLOCK;
		    return ret;
		}
		result = tx->tx_cc;
		qb = &(tx->tx_qbuf);
		qptr = qb->qb_data;
#ifdef ISODEBUG
		if (isodexbug)
			fprintf(stderr, "TReadRequest want %d got %d\n", 
				size, result);
#endif
	} 
#ifdef ISODEBUG
	else {
		if (isodexbug)
			fprintf(stderr, "TReadFromClient want %d buffered %d\n",
				 size, result);
	}
#endif
/*
 * Buffer it
 */
        ingot = 0;
        aptr = data;
	for(ingot = 0, aptr = data, q2data = min(size, result); 
		ingot<q2data;
		aptr += qcpy, ingot+= qcpy) {
	    int aleft = q2data - ingot;
	    if (qb->qb_len > aleft) {
		    qcpy = aleft;
		    bcopy(qptr, aptr, qcpy);
		    qptr += aleft;
	    } else {
		    qcpy = qb->qb_len;
		    bcopy(qb->qb_data, aptr, qcpy);
		    if ((qb = qb->qb_forw) == NULL)
			break;
		    qptr = qb->qb_data;
	    }
        }
	if ((result -= ingot) <= 0) {
		result = 0;
		TXFREE(tx);
	}
	return ingot;
}

#endif /* ISOCONN */

/* We want to read the connection information.  If the client doesn't
 * send us enough data, however, we want to time out eventually.
 * The scheme is to clear a flag, set an alarm, and keep doing non-blocking
 * reads until we get all the data we want. If the alarm goes
 * off, the handler will clear the flag.  If we see that the flag is
 * cleared, we know we've timed out and return with an error.
 *
 * there remains one problem with this code:
 * there is a window of vulnerability in which we might get an alarm
 * even though all the data has come in properly.  This is because I
 * can't atomically clear the alarm.
 * 
 * Anyone who sees how to fix this problem should do so and
 * submit a fix.
 */

jmp_buf	env;
void TimeOut()
{
    longjmp(env, 1);
}
static Bool 
ReadBuffer(conn, buffer, charsWanted)
    long conn;
    char *buffer;
    int charsWanted;
{
    char *bptr = buffer;
    int got, fTimeOut;
    struct itimerval	itv;

    signal(SIGALRM, TimeOut);
    fTimeOut = FALSE;
    /* only 1 alarm, please, not 1 per minute */
    timerclear(&itv.it_interval);
    itv.it_value.tv_sec = TimeOutValue;
    itv.it_value.tv_usec = 0;
    setitimer(ITIMER_REAL, &itv, (struct itimerval *)NULL);
    /* It better not take a full minute to get to the read call */

    while (charsWanted && (fTimeOut = setjmp(env)) == FALSE)
    {
#ifdef ISOCONN
	got = SRead(conn, bptr, charsWanted, NOTOK);
#else /* ISOCONN */
	got = read(conn, bptr, charsWanted);	
#endif /* ISOCONN */
	if (got <= 0)
	    return FALSE;
	if(got > 0)
	{
	    charsWanted -= got;
	    bptr += got;
	    /* Ok, we got something, reset the timer */
 	    itv.it_value.tv_sec = TimeOutValue;
            itv.it_value.tv_usec = 0;
	    setitimer(ITIMER_REAL, &itv, (struct itimerval *)NULL);
	}
    }
    /* disable the timer */
    timerclear(&itv.it_value);
    setitimer(ITIMER_REAL, &itv, (struct itimerval *)NULL);
    /* If we got here and we didn't time out, then return TRUE, because
     * we must have read what we wanted. If we timed out, return FALSE */
    if(fTimeOut && debug_conns)
	ErrorF("Timed out on connection %d\n", conn);
    return (!fTimeOut);
}

#ifdef ISOCONN
/*
 * Who's calling us?
 */
getISOpeername (conn, from, alen) 
int conn;
struct TSAPaddr *from;
int *alen;
{
	struct TSAPdisconnect td;
	if (TGetAddresses (conn, from, NULLTA, &td) == NOTOK) {
		Error(TErrString(td.td_reason));
		return TRUE;
	};
	*alen = sizeof(struct TSAPaddr);
	return FALSE;
}
#endif /* ISOCONN */
/*****************************************************************
 * ClientAuthorized
 *
 *    Sent by the client at connection setup:
 *                typedef struct _xConnClientPrefix {
 *                   CARD8	byteOrder;
 *                   BYTE	pad;
 *                   CARD16	majorVersion, minorVersion;
 *                   CARD16	nbytesAuthProto;    
 *                   CARD16	nbytesAuthString;   
 *                 } xConnClientPrefix;
 *
 *     	It is hoped that eventually one protocol will be agreed upon.  In the
 *        mean time, a server that implements a different protocol than the
 *        client expects, or a server that only implements the host-based
 *        mechanism, will simply ignore this information.
 *
 *****************************************************************/

int 
ClientAuthorized(conn, pswapped, reason)
    long conn;
    int  *pswapped;
    char **reason;   /* if authorization fails, put reason in here */
{
    short slen;
    union {
	struct sockaddr sa;
#ifdef UNIXCONN
	struct sockaddr_un un;
#endif /* UNIXCONN */
#ifdef TCPCONN
	struct sockaddr_in in;
#endif /* TCPCONN */
#ifdef DNETCONN
	struct sockaddr_dn dn;
#endif /* DNETCONN */
#ifdef ISOCONN
	struct TSAPaddr ts;
#endif /* ISOCONN */
    } from;
    int	fromlen;
    xConnClientPrefix xccp;
    char auth_proto[100];
    char auth_string[100];

#ifdef ISOCONN
/*
 * For now we always auth an ISO client!!
 * should use directory etc etc
 */
    *reason = 0;
#endif /* ISOCONN */
    if (!ReadBuffer(conn, (char *)&xccp, sizeof(xConnClientPrefix)))
    {
	/* If they can't even give us this much, just blow them off
	 * without an error message */
	*reason = 0;
        return 0;
    }
    if (xccp.byteOrder != whichByteIsFirst)
    {        
	SwapConnClientPrefix(&xccp);
	*pswapped = TRUE;
    }
    else
        *pswapped = FALSE;
    if ((xccp.majorVersion != X_PROTOCOL) ||
	(xccp.minorVersion != X_PROTOCOL_REVISION))
    {        
#define STR "Protocol version mismatch"
        *reason = (char *)xalloc(sizeof(STR));
        strcpy(*reason, STR);
	if (debug_conns)
	    ErrorF("%s\n", STR);
#undef STR
        return 0;
    }
    fromlen = sizeof (from);
#ifdef ISOCONN
    if (SGetPeerName (conn, &(from.ts), &fromlen) ||
        InvalidHost (&(from.ts), fromlen)) 
#else /* ISOCONN */
    if (getpeername (conn, &from.sa, &fromlen) ||
        InvalidHost (&from.sa, fromlen)) 
#endif /* ISOCONN */
    {
#define STR "Server is not authorized to connect to host"	
        *reason = (char *)xalloc(sizeof(STR));
        strcpy(*reason, STR);
#undef STR
        return 0;
    }
    
   slen = (xccp.nbytesAuthProto + 3) & ~3;  
    if ( slen )
        if (!ReadBuffer(conn, auth_proto, slen))
        {
#define STR "Length error in xConnClientPrefix for protocol authorization "
            *reason = (char *)xalloc(sizeof(STR));
            strcpy(*reason, STR);
            return 0;
#undef STR
	}
    auth_proto[slen] = '\0';

    slen = (xccp.nbytesAuthString + 3) & ~3;   
    if ( slen)
        if (!ReadBuffer(conn, auth_string, slen))
        {
#define STR "Length error in xConnClientPrefix for protocol string"
            *reason = (char *)xalloc(sizeof(STR));
            strcpy(*reason, STR);
            return 0;
#undef STR
	}
    auth_string[slen] = '\0';

    /* At this point, if the client is authorized to change the access control
     * list, we should getpeername() information, and add the client to
     * the selfhosts list.  It's not really the host machine, but the
     * true purpose of the selfhosts list is to see who may change the
     * access control list.
     */
    return(1);
}    

static int padlength[4] = {0, 3, 2, 1};

/*****************
 * EstablishNewConnections
 *    If anyone is waiting on listened sockets, accept them.
 *    Returns a mask with indices of new clients.  Updates AllClients
 *    and AllSockets.
 *****************/

void
#ifdef ISOCONN
EstablishNewConnections(newclients, nnew, vecp, vec)
    ClientPtr	        *newclients;
    int 		*nnew;
    int			vecp;
    char 		**vec;
#else /* ISOCONN */
EstablishNewConnections(newclients, nnew)
    ClientPtr	        *newclients;
    int 		*nnew;
#endif /* ISOCONN */
{
    long readyconnections;     /* mask of listeners that are ready */
    long curconn;                  /* fd of listener that's ready */
    long newconn;                  /* fd of new client */
    int	 swapped;		/* set by ClientAuthorized if connection is
				 * swapped */
    char *reason;
    struct iovec iov[2];
#ifdef ISOCONN
    struct udvec uv[3];
    struct TSAPdisconnect tds;
    struct TSAPdisconnect *td = &tds;
    struct TSAPstart tsts;
    struct TSAPstart *tst = &tsts;
#endif /* ISOCONN */
    char p[3];

#ifdef TCP_NODELAY
    union {
	struct sockaddr sa;
#ifdef UNIXCONN
	struct sockaddr_un un;
#endif /* UNIXCONN */
#ifdef TCPCONN
	struct sockaddr_in in;
#endif /* TCPCONN */
#ifdef DNETCONN
	struct sockaddr_dn dn;
#endif /* DNETCONN */
    } from;
    int	fromlen;
#endif TCP_NODELAY

    *nnew = 0;
    if (readyconnections = (LastSelectMask[0] & WellKnownConnections)) 
    {
	while (readyconnections) 
	{
	    curconn = ffs (readyconnections) - 1;
#ifdef ISOCONN 
/*
 * At this point, a TAccept has finished with a vec > 0
 * so we need to init them...
 */
	    if ((newconn = SAccept(curconn, vecp, vec)) >= 0)
	    {
		fd2family[newconn] = ISODE_IO;
#else /* ISOCONN */
	    if ((newconn = accept (curconn,
				  (struct sockaddr *) NULL, 
				  (int *)NULL)) >= 0) 
	    {
		fd2family[newconn] = UNIX_IO;
#endif /* ISOCONN */
		if (newconn >= lastfdesc)
		{
		    if (debug_conns)
ErrorF("Didn't make connection: Out of file descriptors for connections\n");
#ifdef ISOCONN
		    SClose(newconn);
#else /* ISOCONN */
		    close (newconn);
#endif /* ISOCONN */
		} 
		else 
		{
		    ClientPtr next = (ClientPtr)NULL;

#ifdef TCP_NODELAY
#ifdef ISOCONN
		    if (fd2family(newconn) == UNIX_IO) 
		    {
#endif /* ISOCONN */
		    fromlen = sizeof (from);
                    if (!getpeername (newconn, &from.sa, &fromlen))
		    {
			if (fromlen && (from.sa.sa_family == AF_INET)) 
			{
			    int mi = 1;
			    setsockopt (newconn, IPPROTO_TCP, TCP_NODELAY,
				       (char *)&mi, sizeof (int));
			}
		    }
#ifdef ISOCONN
		    }
#endif /* ISOCONN */
#endif /* TCP_NODELAY */
		    if (ClientAuthorized(newconn, &swapped, &reason))
		    {
#ifdef	hpux
			/*
			 * HPUX does not have  FNDELAY
			 */
		        {
			    int	arg;
			    arg = 1;
			    ioctl(newconn, FIOSNBIO, &arg);
		        }
#else
		        fcntl (newconn, F_SETFL, FNDELAY);
#endif /* hpux */
			inputBuffers[newconn].used = 1; 
                        if (! inputBuffers[newconn].size) 
			{
			    inputBuffers[newconn].buffer = 
					(char *)xalloc(BUFSIZE);
			    inputBuffers[newconn].size = BUFSIZE;
			    inputBuffers[newconn].bufptr = 	
					inputBuffers[newconn].buffer;
			}
			if (GrabDone)
			{
			    BITSET(SavedAllClients, newconn);
			    BITSET(SavedAllSockets, newconn);
		        }
			else
			{
			    BITSET(AllClients, newconn);
			    BITSET(AllSockets, newconn);
		        }
			next = NextAvailableClient();
			if (next != (ClientPtr)NULL)
			{
			   OsCommPtr priv;

			   newclients[(*nnew)++] = next;
			   next->swapped = swapped;
			   ConnectionTranslation[newconn] = next;
			   priv =  (OsCommPtr)xalloc(sizeof(OsCommRec));
			   priv->fd = newconn;
			   priv->buf = (unsigned char *)
					xalloc(OutputBufferSize);
			   priv->bufsize = OutputBufferSize;
			   priv->count = 0;
			   next->osPrivate = (pointer)priv;
		        }
			else
			{
#define STR "Maximum number of clients exceeded"
			   reason = (char *)xalloc(sizeof(STR));
			   strcpy(reason, STR);
#undef STR
			}
		    }
		    if (next == (ClientPtr)NULL)
		    {
			xConnSetupPrefix c;

			if(reason)
			{
			    c.success = xFalse;
			    c.lengthReason = strlen(reason);
			    c.length = (c.lengthReason + 3) >> 2;
			    c.majorVersion = X_PROTOCOL;
			    c.minorVersion = X_PROTOCOL_REVISION;
			    if(swapped)
			    {
				int	n;

				swaps(&c.majorVersion, n);
				swaps(&c.minorVersion, n);
				swaps(&c.length, n);
			    }

#ifdef ISOCONN
			    (void)SWrite(newconn, (char *)&c, sizeof(xConnSetupPrefix)); 
#else /* ISOCONN */
			    write(newconn, (char *)&c, sizeof(xConnSetupPrefix)); 
#endif /* ISOCONN */
			    iov[0].iov_len = c.lengthReason;
			    iov[0].iov_base = reason;
			    iov[1].iov_len = padlength[c.lengthReason & 3];
			    iov[1].iov_base = p;
#ifdef ISOCONN
			    SWritev(newconn, iov, 2);
#else /* ISOCONN */
			    writev(newconn, iov, 2);
#endif /* ISOCONN */
			    if (debug_conns)
			        ErrorF("Didn't make connection:%s\n", reason);
			}
#ifdef ISOCONN
			SClose(newconn);
#else /* ISOCONN */
			close(newconn);
#endif /* ISOCONN */
			xfree(reason);
		    }

		}
	    }
	    readyconnections &= ~(1 << curconn);
	}
    }
}

/************
 *   CloseDownFileDescriptor:
 *     Remove this file descriptor and it's inputbuffers, etc.
 ************/

void
CloseDownFileDescriptor(connection)
    int connection;
{
#ifdef ISOCONN
    struct TSAPdisconnect tds;
#ifdef ISODEBUG
	if (isodexbug)
		fprintf(stderr, "server: TDiscReq\n");
#endif /* ISODEBUG */
    SClose(connection);
#else /* ISOCONN */
    close(connection);
#endif /* ISOCONN */

    if (inputBuffers[connection].size)
    {
	xfree(inputBuffers[connection].buffer);
	inputBuffers[connection].buffer = (char *) NULL;
	inputBuffers[connection].bufptr = (char *) NULL;
	inputBuffers[connection].size = 0;
    }
    inputBuffers[connection].bufcnt = 0;
    inputBuffers[connection].lenLastReq = 0;
    inputBuffers[connection].used = 0;

    BITCLEAR(AllSockets, connection);
    BITCLEAR(AllClients, connection);
    BITCLEAR(ClientsWithInput, connection);
    BITCLEAR(ClientsWriteBlocked, connection);
    if (!ANYSET(ClientsWriteBlocked))
    	AnyClientsWriteBlocked = FALSE;
}

/*****************
 * CheckConections
 *    Some connection has died, go find which one and shut it down 
 *    The file descriptor has been closed, but is still in AllClients.
 *    If would truly be wonderful if select() would put the bogus
 *    file descriptors in the exception mask, but nooooo.  So we have
 *    to check each and every socket individually.
 *****************/

void
CheckConnections()
{
    long		mask[mskcnt];
    long		tmask[mskcnt]; 
    register int	curclient;
    int			i;
    struct timeval	notime;
    ClientPtr           bad;
    int r;
#ifdef ISOCONN
    struct TSAPdisconnect tds;
    struct TSAPdisconnect *td = &tds;
    char *vec[4];
    int vecp;
#endif /* ISOCONN */

    notime.tv_sec = 0;
    notime.tv_usec = 0;

    COPYBITS(AllClients, mask);
    for (i=0; i<mskcnt; i++)
    {
        while (mask[i])
    	{
	    curclient = ffs (mask[i]) - 1 + (i << 5);
            CLEARBITS(tmask);
            BITSET(tmask, curclient);
#ifdef ISOCONN
            r = TNetAccept (&vecp, vec,
			curclient + 1, tmask, (int *)NULL, (int *)NULL, 
			OK, td);
            if (r == NOTOK)
            {
		Error(TErrString(td->td_reason));
		Error("TNetAccept");
#else /* ISOCONN */
            r = select (curclient + 1, tmask, (int *)NULL, (int *)NULL, 
			&notime);
            if (r < 0)
            {
#endif /* ISOCONN */
	        if (bad = ConnectionTranslation[curclient])
    		    CloseDownClient(bad);
                else
                    CloseDownFileDescriptor(curclient);
            }
	    BITCLEAR(mask, curclient);
	}
    }	
}


/*****************
 * CloseDownConnection
 *    Delete client from AllClients and free resources 
 *****************/

CloseDownConnection(client)
    ClientPtr client;
{
    OsCommPtr oc = (OsCommPtr)client->osPrivate;

    ConnectionTranslation[oc->fd] = (ClientPtr)NULL;
    CloseDownFileDescriptor(oc->fd);
    if (oc->buf != NULL) /* an Xrealloc may have returned NULL */
	xfree(oc->buf);
    xfree(client->osPrivate);
}


AddEnabledDevice(fd)
    int fd;
{
    EnabledDevices |= (1<<fd);
    BITSET(AllSockets, fd);
}


RemoveEnabledDevice(fd)
    int fd;
{
    EnabledDevices &= ~(1<<fd);
    BITCLEAR(AllSockets, fd);
}

/*****************
 * OnlyListenToOneClient:
 *    Only accept requests from  one client.  Continue to handle new
 *    connections, but don't take any protocol requests from the new
 *    ones.  Note that if GrabDone is set, EstablishNewConnections
 *    needs to put new clients into SavedAllSockets and SavedAllClients.
 *    Note also that there is no timeout for this in the protocol.
 *    This routine is "undone" by ListenToAllClients()
 *****************/

OnlyListenToOneClient(client)
    ClientPtr client;
{
    OsCommPtr oc = (OsCommPtr)client->osPrivate;
    int connection = oc->fd;

    if (! GrabDone)
    {
	COPYBITS (ClientsWithInput, SavedClientsWithInput);
        BITCLEAR (SavedClientsWithInput, connection);
	if (GETBIT(ClientsWithInput, connection))
	{
	    CLEARBITS(ClientsWithInput);	    
	    BITSET(ClientsWithInput, connection);
	}
	else
        {
	    CLEARBITS(ClientsWithInput);	    
	}
	COPYBITS(AllSockets, SavedAllSockets);
	COPYBITS(AllClients, SavedAllClients);

	UNSETBITS(AllSockets, AllClients);
	BITSET(AllSockets, connection);
	CLEARBITS(AllClients);
	BITSET(AllClients, connection);
	GrabDone = TRUE;
    }
}

/****************
 * ListenToAllClients:
 *    Undoes OnlyListentToOneClient()
 ****************/

ListenToAllClients()
{
    if (GrabDone)
    {
	ORBITS(AllSockets, AllSockets, SavedAllSockets);
	ORBITS(AllClients, AllClients, SavedAllClients);
	ORBITS(ClientsWithInput, ClientsWithInput, SavedClientsWithInput);
	GrabDone = FALSE;
    }	
}