#include "Xos.h"

#include <stdio.h>
#include <pctcp\options.h>
#include <pctcp\error.h>
#include "X.h"
#include "Xmd.h"
#include "misc.h"
#include "osdep.h"
#include "input.h"
#include "opaque.h"
#include "serial.h"

#ifdef XDMCP
#include "Xdmcp.h"

extern int near MitMagicLen;
extern char near * near MitMagicData;
extern char near MitMagicName[18];

extern char *display;
extern char *defaultDisplayClass;

int near xdmcpSocket = -1;
static struct addr	req_sockaddr;
static int		    sessionSocket;
static xdmcp_states	    state;
static CARD32		    SessionID;
static long		    timeOutTime;
static int		    timeOutRtx;
static long		    defaultKeepaliveDormancy = XDM_DEF_DORMANCY;
static long		    keepaliveDormancy = XDM_DEF_DORMANCY;
static CARD16		    DisplayNumber;
static xdmcp_states	    XDM_INIT_STATE = XDM_OFF;
static CARD16		near auth;

#ifdef SERIALNUMBER
CARD32 near myserial = SERIALNUMBER;
long serialTimeout = 0;
#endif

extern in_name near myaddr, near broadcast_addr;
static struct addr  ManagerAddress, BroadcastAddress;

static XdmcpBuffer near buffer;

static void near get_manager_by_name(int, char **, int);

static	void near send_packet();
static	void near timeout(), near restart();

static void near recv_willing_msg(struct addr *, unsigned);
static void near recv_accept_msg(unsigned);
static void near recv_decline_msg(unsigned);
static void near recv_refuse_msg(unsigned);
static void near recv_failed_msg(unsigned);
static void near recv_alive_msg (unsigned);
static void near recv_ann_serial_msg(struct addr *, unsigned);
static void near recv_serial_msg(struct addr *, unsigned);

static	void near send_query_msg();
static	void near send_request_msg();
static	void near send_manage_msg();
static	void near send_keepalive_msg();
static	void near send_serial(xdmOpCode, struct addr *);

static void near XdmcpFatal(), near XdmcpWarning();
static int near XdmcpReadARRAY8(XdmcpBuffer near *, ARRAY8Ptr);
static int near XdmcpCheckAuthentication (ARRAY8Ptr, ARRAY8Ptr);
static int near XdmcpAddAuthorization (ARRAY8Ptr, ARRAY8Ptr);

static short	xdm_udp_port = XDM_UDP_PORT;
extern char _near dispatchTermFlag;

XdmcpUseMsg ()
{
    ErrorF("-query host-name       contact named host for XDMCP\n");
    ErrorF("-broadcast             broadcast for XDMCP\n");
    ErrorF("-indirect host-name    contact named host for indirect XDMCP\n");
    ErrorF("-port port-num         UDP port number to send messages to\n");
    ErrorF("-once                  Terminate server after one session\n");
    ErrorF("-class display-class   specify display class to send in manage\n");
#ifdef notdef
    ErrorF("-displayID display-id  manufacturer display ID for request\n");
#endif
}

int 
XdmcpOptions(argc, argv, i)
    int	    argc, i;
    char    **argv;
{
    if (strcmp(argv[i], "-query") == 0) {
	get_manager_by_name(argc, argv, ++i);
	XDM_INIT_STATE = XDM_QUERY;
	AccessUsingXdmcp ();
	return (i + 1);
    }
    if (strcmp(argv[i], "-broadcast") == 0) {
	XDM_INIT_STATE = XDM_BROADCAST;
	AccessUsingXdmcp ();
	return (i + 1);
    }
    if (strcmp(argv[i], "-indirect") == 0) {
	get_manager_by_name(argc, argv, ++i);
	XDM_INIT_STATE = XDM_INDIRECT;
	AccessUsingXdmcp ();
	return (i + 1);
    }
    if (strcmp(argv[i], "-port") == 0) {
	++i;
	xdm_udp_port = atoi(argv[i]);
	return (i + 1);
    }
    if (strcmp(argv[i], "-once") == 0) {
	dispatchTermFlag = DE_TERMINATE;
	return (i + 1);
    }
    if (strcmp(argv[i], "-class") == 0) {
	++i;
	defaultDisplayClass = argv[i];
	return (i + 1);
    }
#ifdef notdef
    if (strcmp(argv[i], "-displayID") == 0) {
	++i;
	DisplayID = argv[i];
	return (i + 1);
    }
#endif
    return (i);
}

/* 
 * initialize XDMCP; create the socket, compute the display
 * number, set up the state machine
 */

void near
XdmcpInit()
{
    struct addr addr;

    state = XDM_OFF;	/* initial state */

    /* XXX: Create the socket anyway because we use it for serial number. */
    BroadcastAddress.fhost = broadcast_addr;
    BroadcastAddress.fsocket = xdm_udp_port;
    BroadcastAddress.lsocket = xdm_udp_port;

    xdmcpSocket = net_getdesc();
    if (xdmcpSocket < 0) {
	pneterror("net_getdesc");
	return;
    }

    if (set_option(xdmcpSocket, 0, NET_OPT_NONBLOCKING, (char far *)1, 0)) {
	pneterror("set_option");
	net_release(xdmcpSocket);
	xdmcpSocket = -1;
	return;
    }
    addr.lsocket = xdm_udp_port;
    addr.fhost = 0;
    addr.fsocket = 0;
    if (net_listen(xdmcpSocket, DGRAM, &addr) < 0) {
	pneterror("net_listen");
	net_release(xdmcpSocket);
	xdmcpSocket = -1;
	return;
    }
    state = XDM_INIT_STATE;
    serialTimeout = GetTimeInMillis() + 1;

    if (state != XDM_OFF)
    {
	auth = rand();
	AccessUsingXdmcp();
	timeOutRtx = 0;
	DisplayNumber = (CARD16) atoi(display);
	send_packet();
    }
}

void near
XdmcpReset ()
{
    state = XDM_INIT_STATE;
    if (state != XDM_OFF)
    {
	auth = rand();
    	timeOutRtx = 0;
    	send_packet();
    }
}

/*
 * Called whenever a new connection is created; notices the
 * first connection and saves it to terminate the session
 * when it is closed
 */

void
XdmcpOpenDisplay(sock, addr)
    int	sock;
    unsigned long addr;
{

    if (state != XDM_AWAIT_MANAGE_RESPONSE)
	return;
    state = XDM_RUN_SESSION;
    sessionSocket = sock;
    /* permit access control manipulations from this host */
    AugmentSelf(FamilyInternet, (pointer)&addr);
}

void 
XdmcpCloseDisplay(sock)
    int	sock;
{
    if ((state != XDM_RUN_SESSION && state != XDM_AWAIT_ALIVE_RESPONSE)
	|| sessionSocket != sock)
	    return;
    state = XDM_INIT_STATE;
    dispatchException |= dispatchTermFlag;
    isItTimeToYield = TRUE;
}

/*
 * called before going to sleep, this routine
 * may modify the timeout value about to be sent
 * to select; in this way XDMCP can do appropriate things
 * dynamically while starting up
 */

void near
XdmcpBlockHandler(wt)
    long *wt;
{
    unsigned long when;
    long millisToGo;

    if (timeOutTime == 0)
	when = serialTimeout;
    else if (serialTimeout == 0)
	when = timeOutTime;
    else
	when = min(timeOutTime, serialTimeout);
    if (when == 0)
	return;
    millisToGo = when - GetTimeInMillis() + 1;
    if (millisToGo <= 0) {
	millisToGo = 1;
    }
    if (*wt == 0 || millisToGo < *wt)
	*wt = millisToGo;
}

/*
 * called after timout; this routine will
 * recognise when XDMCP packets await and
 * process them appropriately
 */

/*ARGSUSED*/
void near
XdmcpWakeupHandler()
{
    unsigned long time = GetTimeInMillis();

    if (serialTimeout && time >= serialTimeout)
	send_serial(SERIAL_ANNOUNCE, &BroadcastAddress);
    if (state == XDM_OFF)
	return;
    if (timeOutTime && time >= timeOutTime)
    {
    	if (state == XDM_RUN_SESSION)
    	{
	    state = XDM_KEEPALIVE;
	    send_packet();
    	}
    	else
	    timeout();
    }
}

/*
 * This routine should be called from the routine that drives the
 * user's host menu when the user selects a host
 */

XdmcpSelectHost(host_sockaddr, AuthenticationName)
    struct addr *host_sockaddr;
    ARRAY8Ptr		AuthenticationName;
{
    state = XDM_START_CONNECTION;
    req_sockaddr = *host_sockaddr;
#ifdef notdef
    XdmcpSetAuthentication (AuthenticationName);
#endif
    send_packet();
}

/*
 * !!! this routine should be replaced by a routine that adds
 * the host to the user's host menu. the current version just
 * selects the first host to respond with willing message.
 */

/*ARGSUSED*/
XdmcpAddHost(from, AuthenticationName, hostname, status)
    struct addr *from;
    ARRAY8Ptr		AuthenticationName, hostname, status;
{
    XdmcpSelectHost(from, AuthenticationName);
}

/*
 * A message is queued on the socket; read it and
 * do the appropriate thing
 */

ARRAY8	UnwillingMessage = { (CARD8) 14, (CARD8 *) "Host unwilling" };

void near _fastcall
XdmcpReceivePacket(fd)
int fd;
{
    char xdmcp_buf[128];
    XdmcpHeader	*header;
    struct addr addr;
    int i;

    addr.fhost = 0;
    addr.fsocket = 0;
    addr.lsocket = 0;
    buffer.count = net_read(fd, xdmcp_buf, sizeof (xdmcp_buf), &addr, 0);
    if (buffer.count < 0)
	return;
    buffer.data = xdmcp_buf;
    header = (XdmcpHeader *)xdmcp_buf;
    buffer.data += sizeof (XdmcpHeader);
    buffer.count -= sizeof (XdmcpHeader);
    if (buffer.count < 0)
	return;

    /* reset retransmission backoff */
    timeOutRtx = 0;

    if (header->version != htons(XDM_PROTOCOL_VERSION))
	return;
    
    header->opcode = ntohs(header->opcode);
    header->length = ntohs(header->length);

    switch (header->opcode) {
    case WILLING:
	recv_willing_msg(&addr, header->length);
	break;
    case UNWILLING:
	XdmcpFatal("Manager unwilling", &UnwillingMessage);
	break;
    case ACCEPT:
	recv_accept_msg(header->length);
	break;
    case DECLINE:
	recv_decline_msg(header->length);
	break;
    case REFUSE:
	recv_refuse_msg(header->length);
	break;
    case FAILED:
	recv_failed_msg(header->length);
	break;
    case ALIVE:
	recv_alive_msg(header->length);
	break;
    case SERIAL_ANNOUNCE:
	recv_ann_serial_msg(&addr, header->length);
	break;
    case SERIAL:
	recv_serial_msg(&addr, header->length);
	break;
    }
}

/*
 * send the appropriate message given the current state
 */

static void near
send_packet()
{
    switch (state) {
    case XDM_QUERY:
    case XDM_BROADCAST:
    case XDM_INDIRECT:
	send_query_msg();
	break;
    case XDM_START_CONNECTION:
	send_request_msg();
	break;
    case XDM_MANAGE:
	send_manage_msg();
	break;
    case XDM_KEEPALIVE:
	send_keepalive_msg();
	break;
    }
    timeOutTime = GetTimeInMillis() 
	+ min (XDM_MIN_RTX * (1 << timeOutRtx), XDM_MAX_RTX) * 1000;
}

/*
 * The session is declared dead for some reason; too many
 * timeouts, or Keepalive failure.
 */

XdmcpDeadSession (reason)
    char *reason;
{
    printf ("XDM: %s, declaring session dead\n", reason);
    state = XDM_INIT_STATE;
    isItTimeToYield = TRUE;
    dispatchException |= DE_RESET;
    timeOutTime = 0;
    timeOutRtx = 0;
    send_packet();
}

/*
 * Timeout waiting for an XDMCP response.
 */

static void near
timeout()
{
    timeOutRtx++;
    if (state == XDM_AWAIT_ALIVE_RESPONSE && timeOutRtx >= XDM_KA_RTX_LIMIT )
    {
	XdmcpDeadSession ("too many keepalive retransmissions");
	return;
    }
    else if (timeOutRtx >= XDM_RTX_LIMIT)
    {
	printf("XDM: too many retransmissions\n");
	state = XDM_AWAIT_USER_INPUT;
	timeOutTime = 0;
	timeOutRtx = 0;
	return;
    }

    switch (state) {
    case XDM_COLLECT_QUERY:
	state = XDM_QUERY;
	break;
    case XDM_COLLECT_BROADCAST_QUERY:
	state = XDM_BROADCAST;
	break;
    case XDM_COLLECT_INDIRECT_QUERY:
	state = XDM_INDIRECT;
	break;
    case XDM_AWAIT_REQUEST_RESPONSE:
	state = XDM_START_CONNECTION;
	break;
    case XDM_AWAIT_MANAGE_RESPONSE:
	state = XDM_MANAGE;
	break;
    case XDM_AWAIT_ALIVE_RESPONSE:
	state = XDM_KEEPALIVE;
	break;
    }
    send_packet();
}

static void near
restart()
{
    state = XDM_INIT_STATE;
    timeOutRtx = 0;
    send_packet();
}

static int near
XdmcpCheckAuthentication (Name, Data)
    ARRAY8Ptr	Name, Data;
{
    return (Data->length == sizeof auth) && (*(CARD16 *)Data->data == auth);
}

static int near
XdmcpAddAuthorization (name, data)
    ARRAY8Ptr	name, data;
{
    if (name->length != sizeof MitMagicName ||
	memcmp(name->data, MitMagicName, sizeof MitMagicName))
	    return FALSE;
    MitMagicLen = data->length;
    if (MitMagicData)
	MitMagicData = _nrealloc(MitMagicData, MitMagicLen);
    else
	MitMagicData = _nmalloc(MitMagicLen);
    if (!MitMagicData) {
	MitMagicLen = 0;
	return FALSE;
    }
    memcpy(MitMagicData, data->data, MitMagicLen);
    return TRUE;
}

/*
 * from here to the end of this file are routines private
 * to the state machine.
 */

static void near
send_query_msg()
{
    CARD8 buf[128];
    XdmcpHeader	*header = (XdmcpHeader *)buf;
    CARD8	*cp = (CARD8 *)(header+1);
    Bool	broadcast = FALSE;
    int		i;

    header->version = htons(XDM_PROTOCOL_VERSION);
    switch(state){
    case XDM_QUERY:
	header->opcode = htons(QUERY); 
	state = XDM_COLLECT_QUERY;
	break;
    case XDM_BROADCAST:
	header->opcode = htons(BROADCAST_QUERY);
	state = XDM_COLLECT_BROADCAST_QUERY;
	broadcast = TRUE;
	break;
    case XDM_INDIRECT:
	header->opcode = htons(INDIRECT_QUERY);
	state = XDM_COLLECT_INDIRECT_QUERY;
	break;
    }

    /* Don;t bother with authentication.  xdm doesn't use it on query anyway. */
    *cp++ = 0;

    i = cp - buf;
    header->length = htons(i - sizeof (XdmcpHeader));

    net_writeto(xdmcpSocket, (char *)buf, i,
	broadcast ? &BroadcastAddress : &ManagerAddress,
	broadcast ? NET_FLG_BROADCAST : 0);
}

static void near
recv_willing_msg(from, length)
    struct addr	*from;
    unsigned		length;
{
    ARRAY8	authenticationName;
    ARRAY8	hostname;
    ARRAY8	status;

    if (XdmcpReadARRAY8 (&buffer, &authenticationName) &&
	XdmcpReadARRAY8 (&buffer, &hostname) &&
	XdmcpReadARRAY8 (&buffer, &status))
    {
    	if (length == 6 + authenticationName.length +
		      hostname.length + status.length)
    	{
	    switch (state)
	    {
	    case XDM_COLLECT_QUERY:
	    	XdmcpSelectHost(from, &authenticationName);
	    	break;
	    case XDM_COLLECT_BROADCAST_QUERY:
	    case XDM_COLLECT_INDIRECT_QUERY:
	    	XdmcpAddHost(from, &authenticationName, &hostname, &status);
	    	break;
    	    }
    	}
    }
}

static void near
send_request_msg()
{
    XdmcpHeader	    *header;
    CARD8	buf[256];
    CARD8	*cp;
    int		    i;

    header = (XdmcpHeader *)buf;
    header->version = htons(XDM_PROTOCOL_VERSION);
    header->opcode =  htons(REQUEST);
    /* length later */
    cp = (CARD8 *)(header + 1);

    *(CARD16 *)cp = htons(DisplayNumber);
    cp += sizeof (CARD16);

    /* Write out connection types.  We only have one. */
    *cp++ = 1;
    *(CARD16 *)cp = htons(FamilyInternet);
    cp += sizeof (CARD16);
    *cp++ = 1;
    *cp++ = 0;	/* MSB of sizeof myaddr */
    *cp++ = sizeof myaddr;
    memcpy(cp, &myaddr, sizeof myaddr);	/* It's in network byte order. */
    cp += sizeof myaddr;

    *cp++ = 0;	/* name */
    *cp++ = 9;	/* authenticationName */
    memcpy(cp, "STARNET-1", 9);
    cp += 9;

    *cp++ = 0;
    *cp++ = sizeof auth;	/* authenticationData */
    memcpy(cp, &auth, sizeof auth);
    cp += sizeof auth;

    /* Only MIT-MAGIC-COOKIE-1 */
    *cp++ = 1;
    *cp++ = 0;
    *cp++ = sizeof MitMagicName;
    memcpy(cp, MitMagicName, sizeof MitMagicName);
    cp += sizeof MitMagicName;

#ifdef notdef
    /* DisplayID is used for authentication only, which also uses DES.
     * Since we don't have DES, this probably doesn't matter. */
    i = strlen(DisplayID);
    *cp++ = (CARD8)((unsigned)i >> 8);
    *cp++ = (CARD8)i;	/* DisplayID */
    memcpy(cp, DisplayID, i);
    cp += i;
#else
    *cp++ = 0;
    *cp++ = 0;
#endif

    /* Now we know the length */
    i = cp - buf;
    header->length = htons(i - sizeof (XdmcpHeader));

    net_writeto(xdmcpSocket, (char *)buf, i, &req_sockaddr, 0);
    state = XDM_AWAIT_REQUEST_RESPONSE;
}

static void near
recv_accept_msg(length)
    unsigned		length;
{
    CARD32  AcceptSessionID;
    ARRAY8  AcceptAuthenticationName, AcceptAuthenticationData;
    ARRAY8  AcceptAuthorizationName, AcceptAuthorizationData;

    if (state != XDM_AWAIT_REQUEST_RESPONSE)
	return;
    AcceptSessionID = ntohl(*(CARD32 *)buffer.data);
    buffer.data += sizeof (CARD32);
    buffer.count -= sizeof (CARD32);
    if (XdmcpReadARRAY8 (&buffer, &AcceptAuthenticationName) &&
	XdmcpReadARRAY8 (&buffer, &AcceptAuthenticationData) &&
	XdmcpReadARRAY8 (&buffer, &AcceptAuthorizationName) &&
	XdmcpReadARRAY8 (&buffer, &AcceptAuthorizationData))
    {
    	if (length == 12 + AcceptAuthenticationName.length +
		      	   AcceptAuthenticationData.length +
		      	   AcceptAuthorizationName.length +
 		      	   AcceptAuthorizationData.length)
    	{
	    if (!XdmcpCheckAuthentication (&AcceptAuthenticationName,
				      &AcceptAuthenticationData))
	    {
		XdmcpFatal ("Authentication Failure", &AcceptAuthenticationName);
	    }
	    /* if the authorization specified in the packet fails
	     * to be acceptable, enable the local addresses
	     */
	    if (!XdmcpAddAuthorization (&AcceptAuthorizationName,
					&AcceptAuthorizationData))
	    {
#ifdef notdef
		AddLocalHosts ();
#endif
	    }
	    SessionID = AcceptSessionID;
    	    state = XDM_MANAGE;
    	    send_packet();
    	}
    }
}

static void near
recv_ann_serial_msg(addr, length)
struct addr *addr;
unsigned length;
{
    CARD32 serial;

    if (length != sizeof serial)
	return;
    if (addr->fhost == myaddr)
	return;	/* If this is true, there are other problems. */
    serial = ntohl(*(CARD32 *)buffer.data);
    buffer.data += sizeof (CARD32);
    buffer.count -= sizeof (CARD32);
    if (serial == myserial)
	send_serial(SERIAL, addr);
}

static void near
recv_serial_msg(addr, length)
struct addr *addr;
unsigned length;
{
    CARD32 serial;

    if (length != sizeof serial)
	return;
    if (addr->fhost == myaddr)
	return;	/* If this is true, there are other problems. */
    serial = ntohl(*(CARD32 *)buffer.data);
    buffer.data += sizeof (CARD32);
    buffer.count -= sizeof (CARD32);
    if (serial == myserial)
	FatalError("Duplicate serial number from %d.%d.%d.%d\n", 
		((CARD8 *)&addr->fhost)[0],
		((CARD8 *)&addr->fhost)[1],
		((CARD8 *)&addr->fhost)[2],
		((CARD8 *)&addr->fhost)[3]);
}

static void near
send_serial(type, addr)
xdmOpCode type;
struct addr *addr;
{
    CARD8 buf[32];
    XdmcpHeader	*header = (XdmcpHeader *)buf;
    unsigned long time;

    header->version = htons(XDM_PROTOCOL_VERSION);
    header->opcode = htons(type);
    header->length = htons(sizeof (CARD32));
    *(CARD32 *)(header + 1) = htonl(myserial);
    net_writeto(xdmcpSocket,
	(char *)buf, sizeof (XdmcpHeader) + sizeof (CARD32),
	addr, addr == &BroadcastAddress ? NET_FLG_BROADCAST : 0);
    time = GetTimeInMillis();
    if (serialTimeout < time)
	serialTimeout = time + SERIALTIMEOUT;
}

static void near
recv_decline_msg(length)
    unsigned		length;
{
    ARRAY8  Status, DeclineAuthenticationName, DeclineAuthenticationData;

    if (XdmcpReadARRAY8 (&buffer, &Status) &&
	XdmcpReadARRAY8 (&buffer, &DeclineAuthenticationName) &&
	XdmcpReadARRAY8 (&buffer, &DeclineAuthenticationData))
    {
    	if (length == 6 + Status.length +
		      	  DeclineAuthenticationName.length +
 		      	  DeclineAuthenticationData.length &&
	    XdmcpCheckAuthentication (&DeclineAuthenticationName,
				      &DeclineAuthenticationData))
    	{
	    XdmcpFatal ("Session declined", &Status);
    	}
    }
}

static void near
send_manage_msg()
{
    CARD8 buf[128];
    XdmcpHeader	*header = (XdmcpHeader *)buf;
    CARD8 *cp = (CARD8 *)(header + 1);
    int i;

    header->version = htons(XDM_PROTOCOL_VERSION);
    header->opcode = htons(MANAGE);

    *(CARD32 *)cp = htonl(SessionID);
    cp += sizeof (CARD32);
    *(CARD16 *)cp = htons(DisplayNumber);
    cp += sizeof (CARD16);

    i = strlen(defaultDisplayClass);
    *cp++ = (CARD8)((unsigned)i >> 8);	/* Display Class */
    *cp++ = (CARD8)i;
    memcpy(cp, defaultDisplayClass, i);
    cp += i;

    i = cp - buf;
    header->length = htons(i - sizeof (XdmcpHeader));

    net_writeto(xdmcpSocket, (char *)buf, i, &req_sockaddr, 0);
    state = XDM_AWAIT_MANAGE_RESPONSE;
}

static void near
recv_refuse_msg(length)
    unsigned		length;
{
    CARD32  RefusedSessionID;

    if (state != XDM_AWAIT_MANAGE_RESPONSE)
	return;
    if (length != 4)
	return;
    RefusedSessionID = ntohl(*(CARD32 *)buffer.data);
    if (RefusedSessionID == SessionID)
    {
	state = XDM_START_CONNECTION;
	send_packet();
    }
}

static void near
recv_failed_msg(length)
    unsigned		length;
{
    CARD32  FailedSessionID;
    ARRAY8  Status;

    if (state != XDM_AWAIT_MANAGE_RESPONSE)
	return;
    FailedSessionID = ntohl(*(CARD32 *)buffer.data);
    buffer.data += sizeof (CARD32);
    buffer.count -= sizeof (CARD32);
    if (buffer.count > 0 &&
	XdmcpReadARRAY8 (&buffer, &Status))
    {
    	if (length == 5 + Status.length &&
	    SessionID == FailedSessionID)
	{
	    XdmcpFatal ("Session failed", &Status);
	}
    }
}

static void near
send_keepalive_msg()
{
    CARD8 buf[128];
    XdmcpHeader	*header = (XdmcpHeader *)buf;
    CARD8 *cp = (CARD8 *)(header + 1);

    header->version = htons(XDM_PROTOCOL_VERSION);
    header->opcode = htons(KEEPALIVE);
    header->length = htons(6);

    *(CARD16 *)cp = htons(DisplayNumber);
    cp += sizeof (CARD16);
    *(CARD32 *)cp = htonl(SessionID);
    cp += sizeof (CARD32);

    net_writeto(xdmcpSocket, (char *)buf, cp - buf, &req_sockaddr, 0);

    state = XDM_AWAIT_ALIVE_RESPONSE;
}

static void near
recv_alive_msg (length)
    unsigned		length;
{
    CARD8   SessionRunning;
    CARD32  AliveSessionID;

    if (state != XDM_AWAIT_ALIVE_RESPONSE)
	return;
    if (length != 5)
	return;
    if (buffer.count >= 5) {
	SessionRunning = *buffer.data;
	buffer.data++;
	buffer.count--;
	AliveSessionID = ntohl(*(CARD32 *)buffer.data);

    	if (SessionRunning && AliveSessionID == SessionID)
    	{
	    /* backoff dormancy period */
	    state = XDM_RUN_SESSION;
	    if (TimeSinceLastInputEvent() > keepaliveDormancy * 1000)
	    	keepaliveDormancy = min(keepaliveDormancy << 1, XDM_MAX_DORMANCY);
	    timeOutTime = GetTimeInMillis() + keepaliveDormancy * 1000;
    	}
	else
    	{
	    XdmcpDeadSession ("Alive respose indicates session dead");
    	}
    }
}

static void near
XdmcpFatal (type, status)
    char	*type;
    ARRAY8Ptr	status;
{
    extern void AbortDDX();

    printf("XDMCP fatal error: %s %*.*s\n", type,
	   status->length, status->length, status->data);
    AbortDDX ();
    exit (1);
}

static void near
XdmcpWarning(str)
    char *str;
{
    printf("XDMCP warning: %s\n", str);
}

static void near
get_manager_by_name(argc, argv, i)
    int	    argc, i;
    char    **argv;
{
    unsigned long addr;

    if (i == argc)
    {
	printf("Xserver: missing host name in command line\n");
	exit(1);
    }
    addr = inet_addr(argv[i]);
    if (addr == -1) {
	printf("Xserver: illegal IP address: %s\n", argv[i]);
	exit(1);
    }
    ManagerAddress.fhost = addr;
    ManagerAddress.fsocket = xdm_udp_port;
    ManagerAddress.lsocket = xdm_udp_port;
}

static int near
XdmcpReadARRAY8(buffer, array)
XdmcpBuffer near *buffer;
ARRAY8Ptr array;
{
    int i;

    i = ntohs(*(unsigned short *)buffer->data);
    buffer->count -= sizeof (unsigned short);
    buffer->data += sizeof (unsigned short);
    array->length = i;
    if (buffer->count < i)
	return FALSE;
    if (!i) {
	array->data = 0;
	return TRUE;
    }
    array->data = buffer->data;
    buffer->data += i;
    buffer->count -= i;
    return TRUE;
}
#endif /* XDMCP */
