diff options
Diffstat (limited to 'kdm/backend/choose.c')
-rw-r--r-- | kdm/backend/choose.c | 1051 |
1 files changed, 1051 insertions, 0 deletions
diff --git a/kdm/backend/choose.c b/kdm/backend/choose.c new file mode 100644 index 000000000..ead841228 --- /dev/null +++ b/kdm/backend/choose.c @@ -0,0 +1,1051 @@ +/* + +Copyright 1990, 1998 The Open Group +Copyright 2002-2004 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * chooser backend + */ + +#include <config.h> + +#ifdef XDMCP + +#include "dm.h" +#include "dm_error.h" +#include "dm_socket.h" + +#include <ctype.h> + +#ifdef __svr4__ +# include <sys/sockio.h> +#endif +#include <arpa/inet.h> + +#include <sys/ioctl.h> +#ifdef STREAMSCONN +# ifdef WINTCP /* NCR with Wollongong TCP */ +# include <netinet/ip.h> +# endif +# include <stropts.h> +# include <tiuser.h> +# include <netconfig.h> +# include <netdir.h> +#endif + +#if !defined(__GNU__) && !defined(__hpux) /* XXX __hpux might be wrong */ +# include <net/if.h> +#endif + +#include <netdb.h> + +typedef struct _IndirectUsers { + struct _IndirectUsers *next; + ARRAY8 client; + CARD16 connectionType; +} IndirectUsersRec, *IndirectUsersPtr; + +static IndirectUsersPtr indirectUsers; + +int +RememberIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + IndirectUsersPtr i; + + for (i = indirectUsers; i; i = i->next) + if (XdmcpARRAY8Equal( clientAddress, &i->client ) && + connectionType == i->connectionType) + return 1; + if (!(i = (IndirectUsersPtr)Malloc( sizeof(IndirectUsersRec) ))) + return 0; + if (!XdmcpCopyARRAY8( clientAddress, &i->client )) { + free( (char *)i ); + return 0; + } + i->connectionType = connectionType; + i->next = indirectUsers; + indirectUsers = i; + return 1; +} + +void +ForgetIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + IndirectUsersPtr *i, ni; + + for (i = &indirectUsers; *i; i = &(*i)->next) + if (XdmcpARRAY8Equal( clientAddress, &(*i)->client ) && + connectionType == (*i)->connectionType) + { + ni = (*i)->next; + XdmcpDisposeARRAY8( &(*i)->client ); + free( (char *)(*i) ); + (*i) = ni; + break; + } +} + +int +IsIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + IndirectUsersPtr i; + + for (i = indirectUsers; i; i = i->next) + if (XdmcpARRAY8Equal( clientAddress, &i->client ) && + connectionType == i->connectionType) + return 1; + return 0; +} + +typedef struct _Choices { + struct _Choices *next; + ARRAY8 client; + CARD16 connectionType; + ARRAY8 choice; + Time_t time; +} ChoiceRec, *ChoicePtr; + +static ChoicePtr choices; + +ARRAY8Ptr +IndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + ChoicePtr c, next, prev; + + prev = 0; + for (c = choices; c; c = next) { + next = c->next; + Debug( "choice checking timeout: %ld >? %d\n", + (long)(now - c->time), choiceTimeout ); + if (now - c->time > (Time_t)choiceTimeout) { + Debug( "timeout choice %ld > %d\n", + (long)(now - c->time), choiceTimeout ); + if (prev) + prev->next = next; + else + choices = next; + XdmcpDisposeARRAY8( &c->client ); + XdmcpDisposeARRAY8( &c->choice ); + free( (char *)c ); + } else { + if (XdmcpARRAY8Equal( clientAddress, &c->client ) && + connectionType == c->connectionType) + return &c->choice; + prev = c; + } + } + return 0; +} + +int +RegisterIndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType, + ARRAY8Ptr choice ) +{ + ChoicePtr c; + int insert; +#if 0 + int found = 0; +#endif + + Debug( "got indirect choice back\n" ); + for (c = choices; c; c = c->next) { + if (XdmcpARRAY8Equal( clientAddress, &c->client ) && + connectionType == c->connectionType) { +#if 0 + found = 1; +#endif + break; + } + } +#if 0 + if (!found) + return 0; +#endif + + insert = 0; + if (!c) { + insert = 1; + c = (ChoicePtr)Malloc( sizeof(ChoiceRec) ); + if (!c) + return 0; + c->connectionType = connectionType; + if (!XdmcpCopyARRAY8( clientAddress, &c->client )) { + free( (char *)c ); + return 0; + } + } else + XdmcpDisposeARRAY8( &c->choice ); + if (!XdmcpCopyARRAY8( choice, &c->choice )) { + XdmcpDisposeARRAY8( &c->client ); + free( (char *)c ); + return 0; + } + if (insert) { + c->next = choices; + choices = c; + } + c->time = now; + return 1; +} + +#if 0 +static void +RemoveIndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + ChoicePtr c, prev; + + prev = 0; + for (c = choices; c; c = c->next) { + if (XdmcpARRAY8Equal( clientAddress, &c->client ) && + connectionType == c->connectionType) + { + if (prev) + prev->next = c->next; + else + choices = c->next; + XdmcpDisposeARRAY8( &c->client ); + XdmcpDisposeARRAY8( &c->choice ); + free( (char *)c ); + return; + } + prev = c; + } +} +#endif + + +/* ####################### */ + + +typedef struct _hostAddr { + struct _hostAddr *next; + struct sockaddr *addr; + int addrlen; + xdmOpCode type; +} HostAddr; + +static HostAddr *hostAddrdb; + +typedef struct _hostName { + struct _hostName *next; + unsigned willing:1, alive:1; + ARRAY8 hostname, status; + CARD16 connectionType; + ARRAY8 hostaddr; +} HostName; + +static HostName *hostNamedb; + +static XdmcpBuffer directBuffer, broadcastBuffer; +static XdmcpBuffer buffer; + +static int socketFD; +#if defined(IPv6) && defined(AF_INET6) +static int socket6FD; +#endif + + +static void +doPingHosts() +{ + HostAddr *hosts; + + for (hosts = hostAddrdb; hosts; hosts = hosts->next) +#if defined(IPv6) && defined(AF_INET6) + XdmcpFlush( hosts->addr->sa_family == AF_INET6 ? socket6FD : socketFD, +#else + XdmcpFlush( socketFD, +#endif + hosts->type == QUERY ? &directBuffer : &broadcastBuffer, + (XdmcpNetaddr)hosts->addr, hosts->addrlen ); +} + +static int +addHostname( ARRAY8Ptr hostname, ARRAY8Ptr status, + struct sockaddr *addr, int will ) +{ + HostName **names, *name; + ARRAY8 hostAddr; + CARD16 connectionType; + + switch (addr->sa_family) { + case AF_INET: + hostAddr.data = + (CARD8 *)& ((struct sockaddr_in *)addr)->sin_addr; + hostAddr.length = 4; + connectionType = FamilyInternet; + break; +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + hostAddr.data = + (CARD8 *)&((struct sockaddr_in6 *)addr)->sin6_addr; + hostAddr.length = 16; + connectionType = FamilyInternet6; + break; +#endif + default: + hostAddr.data = (CARD8 *)""; + hostAddr.length = 0; + connectionType = FamilyLocal; + break; + } + for (names = &hostNamedb; *names; names = &(*names)->next) { + name = *names; + if (connectionType == name->connectionType && + XdmcpARRAY8Equal( &hostAddr, &name->hostaddr )) + { + if (XdmcpARRAY8Equal( status, &name->status )) + return 0; + XdmcpDisposeARRAY8( &name->status ); + XdmcpDisposeARRAY8( hostname ); + + GSendInt( G_Ch_ChangeHost ); + goto gotold; + } + } + if (!(name = (HostName *)Malloc( sizeof(*name) ))) + return 0; + if (hostname->length) { + switch (addr->sa_family) { + case AF_INET: +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: +#endif + { + struct hostent *hostent; + char *host; + + hostent = gethostbyaddr( (char *)hostAddr.data, + hostAddr.length, addr->sa_family ); + if (hostent) { + XdmcpDisposeARRAY8( hostname ); + host = hostent->h_name; + XdmcpAllocARRAY8( hostname, strlen( host ) ); + memmove( hostname->data, host, hostname->length ); + } + } + } + } + if (!XdmcpAllocARRAY8( &name->hostaddr, hostAddr.length )) { + free( (char *)name ); + return 0; + } + memmove( name->hostaddr.data, hostAddr.data, hostAddr.length ); + name->connectionType = connectionType; + name->hostname = *hostname; + + *names = name; + name->next = 0; + + GSendInt( G_Ch_AddHost ); + gotold: + name->alive = 1; + name->willing = will; + name->status = *status; + + GSendInt( (int)name ); /* just an id */ + GSendNStr( (char *)name->hostname.data, name->hostname.length ); + GSendNStr( (char *)name->status.data, name->status.length ); + GSendInt( will ); + + return 1; +} + +static void +disposeHostname( HostName *host ) +{ + XdmcpDisposeARRAY8( &host->hostname ); + XdmcpDisposeARRAY8( &host->hostaddr ); + XdmcpDisposeARRAY8( &host->status ); + free( (char *)host ); +} + +static void +emptyHostnames( void ) +{ + HostName *host, *nhost; + + for (host = hostNamedb; host; host = nhost) { + nhost = host->next; + disposeHostname( host ); + } + hostNamedb = 0; +} + +static void +receivePacket( int sfd ) +{ + XdmcpHeader header; + ARRAY8 authenticationName; + ARRAY8 hostname; + ARRAY8 status; + int saveHostname = 0; +#if defined(IPv6) && defined(AF_INET6) + struct sockaddr_storage addr; +#else + struct sockaddr addr; +#endif + int addrlen; + + addrlen = sizeof(addr); + if (!XdmcpFill( sfd, &buffer, (XdmcpNetaddr)&addr, &addrlen )) + return; + if (!XdmcpReadHeader( &buffer, &header )) + return; + if (header.version != XDM_PROTOCOL_VERSION) + return; + hostname.data = 0; + status.data = 0; + authenticationName.data = 0; + switch (header.opcode) { + case WILLING: + if (XdmcpReadARRAY8( &buffer, &authenticationName ) && + XdmcpReadARRAY8( &buffer, &hostname ) && + XdmcpReadARRAY8( &buffer, &status )) { + if (header.length == 6 + authenticationName.length + + hostname.length + status.length) { + if (addHostname( &hostname, &status, + (struct sockaddr *)&addr, 1 )) + saveHostname = 1; + } + } + XdmcpDisposeARRAY8( &authenticationName ); + break; + case UNWILLING: + if (XdmcpReadARRAY8( &buffer, &hostname ) && + XdmcpReadARRAY8( &buffer, &status )) { + if (header.length == 4 + hostname.length + status.length) { + if (addHostname( &hostname, &status, + (struct sockaddr *)&addr, 0 )) + saveHostname = 1; + } + } + break; + default: + break; + } + if (!saveHostname) { + XdmcpDisposeARRAY8( &hostname ); + XdmcpDisposeARRAY8( &status ); + } +} + +static void +addHostaddr( HostAddr **hosts, struct sockaddr *addr, int len, xdmOpCode type ) +{ + HostAddr *host; + + Debug( "adding host %[*hhu, type %d\n", len, addr, type ); + for (host = *hosts; host; host = host->next) + if (host->type == type && host->addr->sa_family == addr->sa_family) + switch (addr->sa_family) { + case AF_INET: + { + struct sockaddr_in *na = (struct sockaddr_in *)addr; + struct sockaddr_in *oa = (struct sockaddr_in *)host->addr; + if (na->sin_port == oa->sin_port && + na->sin_addr.s_addr == oa->sin_addr.s_addr) + return; + break; + } +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + { + struct sockaddr_in6 *na = (struct sockaddr_in6 *)addr; + struct sockaddr_in6 *oa = (struct sockaddr_in6 *)host->addr; + if (na->sin6_port == oa->sin6_port && + !memcmp( &na->sin6_addr, &oa->sin6_addr, 16 )) + return; + break; + } +#endif + default: /* ... */ + break; + } + Debug( " not dupe\n" ); + if (!(host = (HostAddr *)Malloc( sizeof(*host) ))) + return; + if (!(host->addr = (struct sockaddr *)Malloc( len ))) { + free( (char *)host ); + return; + } + memcpy( (char *)host->addr, (char *)addr, len ); + host->addrlen = len; + host->type = type; + host->next = *hosts; + *hosts = host; +} + +static void +registerHostaddr( struct sockaddr *addr, int len, xdmOpCode type ) +{ + addHostaddr( &hostAddrdb, addr, len, type ); +} + +static void +emptyPingHosts( void ) +{ + HostAddr *host, *nhost; + + for (host = hostAddrdb; host; host = nhost) { + nhost = host->next; + free( host->addr ); + free( host ); + } + hostAddrdb = 0; +} + +/* Handle variable length ifreq in BNR2 and later */ +#ifdef VARIABLE_IFREQ +# define ifr_size(p) \ + (sizeof(struct ifreq) + \ + (p->ifr_addr.sa_len > sizeof (p->ifr_addr) ? \ + p->ifr_addr.sa_len - sizeof (p->ifr_addr) : 0)) +#else +# define ifr_size(p) (sizeof(struct ifreq)) +#endif + +#define IFC_REQ(ifc) ifc.ifc_req + +#ifndef SYSV_SIOCGIFCONF +# define ifioctl ioctl +#endif + +static void +registerBroadcastForPing( void ) +{ + struct sockaddr_in in_addr; + +#ifdef __GNU__ + in_addr.sin_addr.s_addr = htonl( 0xFFFFFFFF ); + in_addr.sin_port = htons( XDM_UDP_PORT ); + registerHostaddr( (struct sockaddr *)&in_addr, sizeof(in_addr), + BROADCAST_QUERY ); +#else /* __GNU__ */ + struct ifconf ifc; + register struct ifreq *ifr; + struct sockaddr broad_addr; + char buf[2048], *cp, *cplim; +# ifdef WINTCP /* NCR with Wollongong TCP */ + int ipfd; + struct ifconf *ifcp; + struct strioctl ioc; + int n; + + ifcp = (struct ifconf *)buf; + ifcp->ifc_buf = buf + 4; + ifcp->ifc_len = sizeof(buf) - 4; + + if ((ipfd = open( "/dev/ip", O_RDONLY )) < 0) { + t_error( "RegisterBroadcastForPing() t_open(/dev/ip) failed" ); + return; + } + + ioc.ic_cmd = IPIOC_GETIFCONF; + ioc.ic_timout = 60; + ioc.ic_len = sizeof(buf); + ioc.ic_dp = (char *)ifcp; + + if (ioctl( ipfd, (int)I_STR, (char *)&ioc ) < 0) { + perror( "RegisterBroadcastForPing() ioctl(I_STR(IPIOC_GETIFCONF)) failed" ); + close( ipfd ); + return; + } + + for (ifr = ifcp->ifc_req, n = ifcp->ifc_len / sizeof(struct ifreq); + --n >= 0; ifr++) +# else /* WINTCP */ + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ifioctl(socketFD, (int)SIOCGIFCONF, (char *)&ifc) < 0) + return; + + cplim = (char *)IFC_REQ( ifc ) + ifc.ifc_len; + + for (cp = (char *)IFC_REQ( ifc ); cp < cplim; cp += ifr_size(ifr)) +# endif /* WINTCP */ + { +# ifndef WINTCP + ifr = (struct ifreq *)cp; +# endif + if (ifr->ifr_addr.sa_family != AF_INET) + continue; + + broad_addr = ifr->ifr_addr; + ((struct sockaddr_in *)&broad_addr)->sin_addr.s_addr = + htonl( INADDR_BROADCAST ); +# ifdef SIOCGIFBRDADDR + { + struct ifreq broad_req; + + broad_req = *ifr; +# ifdef WINTCP /* NCR with Wollongong TCP */ + ioc.ic_cmd = IPIOC_GETIFFLAGS; + ioc.ic_timout = 0; + ioc.ic_len = sizeof(broad_req); + ioc.ic_dp = (char *)&broad_req; + + if (ioctl (ipfd, I_STR, (char *) &ioc ) != -1 && +# else /* WINTCP */ + if (ifioctl( socketFD, SIOCGIFFLAGS, (char *)&broad_req ) != + -1 && +# endif /* WINTCP */ + (broad_req.ifr_flags & IFF_BROADCAST) && + (broad_req.ifr_flags & IFF_UP)) + { + broad_req = *ifr; +# ifdef WINTCP /* NCR with Wollongong TCP */ + ioc.ic_cmd = IPIOC_GETIFBRDADDR; + ioc.ic_timout = 0; + ioc.ic_len = sizeof(broad_req); + ioc.ic_dp = (char *)&broad_req; + + if (ioctl( ipfd, I_STR, (char *) &ioc) != -1 ) +# else /* WINTCP */ + if (ifioctl( socketFD, SIOCGIFBRDADDR, (char *)&broad_req ) + != -1) +# endif /* WINTCP */ + broad_addr = broad_req.ifr_addr; + else + continue; + } else + continue; + } +# endif + in_addr = *((struct sockaddr_in *)&broad_addr); + in_addr.sin_port = htons( XDM_UDP_PORT ); +# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + in_addr.sin_len = sizeof(in_addr); +# endif + registerHostaddr( (struct sockaddr *)&in_addr, sizeof(in_addr), + BROADCAST_QUERY ); + } +#endif +} + +static int +makeSockAddrs( const char *name, HostAddr **hosts ) +{ +#if defined(IPv6) && defined(AF_INET6) + struct addrinfo *ai, *nai, hints; + bzero( &hints, sizeof(hints) ); + hints.ai_socktype = SOCK_DGRAM; + if (getaddrinfo( name, stringify( XDM_UDP_PORT ), &hints, &ai )) + return 0; + for (nai = ai; nai; nai = nai->ai_next) + if ((nai->ai_family == AF_INET) || (nai->ai_family == AF_INET6)) + addHostaddr( hosts, nai->ai_addr, nai->ai_addrlen, + (nai->ai_family == AF_INET ? + IN_MULTICAST( ((struct sockaddr_in *)nai->ai_addr)->sin_addr.s_addr ) : + IN6_IS_ADDR_MULTICAST( &((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr )) ? + BROADCAST_QUERY : QUERY ); +#else + struct sockaddr_in in_addr; + /* Per RFC 1123, check first for IP address in dotted-decimal form */ + if ((in_addr.sin_addr.s_addr = inet_addr( name )) == (unsigned)-1) { + struct hostent *hostent; + if (!(hostent = gethostbyname( name )) || + hostent->h_addrtype != AF_INET) + return 0; + memcpy( &in_addr.sin_addr, hostent->h_addr, 4 ); + } + in_addr.sin_family = AF_INET; + in_addr.sin_port = htons( XDM_UDP_PORT ); +# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + in_addr.sin_len = sizeof(in_addr); +# endif + addHostaddr( hosts, (struct sockaddr *)&in_addr, sizeof(in_addr), +# ifdef IN_MULTICAST + IN_MULTICAST( in_addr.sin_addr.s_addr ) ? + BROADCAST_QUERY : +# endif + QUERY ); +#endif + return 1; +} + +/* + * Register the address for this host. + * Called for interactively specified hosts. + * The special names "BROADCAST" and "*" look up all the broadcast + * addresses on the local host. + */ + +static int +registerForPing( const char *name ) +{ + Debug( "manual host registration: %s\n", name ); + if (!strcmp( name, "BROADCAST" ) || !strcmp( name, "*" )) + registerBroadcastForPing(); + else if (!makeSockAddrs( name, &hostAddrdb )) + return 0; + return 1; +} + +/*ARGSUSED*/ +static void +AddChooserHost( + CARD16 connectionType, + ARRAY8Ptr addr, + char *closure ATTR_UNUSED ) +{ + if (connectionType == FamilyBroadcast) { + registerBroadcastForPing(); + return; + } + Debug( "internal host registration: %[*hhu, family %hx\n", + addr->length, addr->data, connectionType ); + if (connectionType == FamilyInternet) { + struct sockaddr_in in_addr; + in_addr.sin_family = AF_INET; + memmove( &in_addr.sin_addr, addr->data, 4 ); + in_addr.sin_port = htons( XDM_UDP_PORT ); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + in_addr.sin_len = sizeof(in_addr); +#endif + registerHostaddr( (struct sockaddr *)&in_addr, sizeof(in_addr), +#ifdef IN_MULTICAST + IN_MULTICAST( in_addr.sin_addr.s_addr ) ? + BROADCAST_QUERY : +#endif + QUERY ); + } +#if defined(IPv6) && defined(AF_INET6) + else if (connectionType == FamilyInternet6) { + struct sockaddr_in6 in6_addr; + in6_addr.sin6_family = AF_INET6; + memmove( &in6_addr.sin6_addr, addr->data, 16 ); + in6_addr.sin6_port = htons( XDM_UDP_PORT ); +#ifdef SIN6_LEN + in6_addr.sin6_len = sizeof(in6_addr); +#endif + registerHostaddr( (struct sockaddr *)&in6_addr, sizeof(in6_addr), + IN6_IS_ADDR_MULTICAST( &in6_addr.sin6_addr ) ? + BROADCAST_QUERY : QUERY ); + } +#endif +} + +static ARRAYofARRAY8 AuthenticationNames; + +#if 0 +static void RegisterAuthenticationName( char *name, int namelen ) +{ + ARRAY8Ptr authName; + if (!XdmcpReallocARRAYofARRAY8( &AuthenticationNames, + AuthenticationNames.length + 1 )) + return; + authName = &AuthenticationNames.data[AuthenticationNames.length - 1]; + if (!XdmcpAllocARRAY8( authName, namelen )) + return; + memmove( authName->data, name, namelen ); +} +#endif + +static int +initXDMCP() +{ + XdmcpHeader header; +#if 0 + int i; +#endif +#ifndef STREAMSCONN +#ifdef SO_BROADCAST + int soopts; +#endif +#endif + + header.version = XDM_PROTOCOL_VERSION; + header.length = 1; +#if 0 + for (i = 0; i < (int)AuthenticationNames.length; i++) + header.length += 2 + AuthenticationNames.data[i].length; +#endif + + header.opcode = (CARD16)BROADCAST_QUERY; + XdmcpWriteHeader( &broadcastBuffer, &header ); + XdmcpWriteARRAYofARRAY8( &broadcastBuffer, &AuthenticationNames ); + + header.opcode = (CARD16)QUERY; + XdmcpWriteHeader( &directBuffer, &header ); + XdmcpWriteARRAYofARRAY8( &directBuffer, &AuthenticationNames ); + +#if defined(STREAMSCONN) + if ((socketFD = t_open( "/dev/udp", O_RDWR, 0 )) < 0) + return 0; + + if (t_bind( socketFD, NULL, NULL ) < 0) { + t_close( socketFD ); + return 0; + } + + /* + * This part of the code looks contrived. It will actually fit in nicely + * when the CLTS part of Xtrans is implemented. + */ + { + struct netconfig *nconf; + + if ((nconf = getnetconfigent( "udp" )) == NULL) { + t_unbind( socketFD ); + t_close( socketFD ); + return 0; + } + + if (netdir_options( nconf, ND_SET_BROADCAST, socketFD, NULL )) { + freenetconfigent( nconf ); + t_unbind( socketFD ); + t_close( socketFD ); + return 0; + } + + freenetconfigent( nconf ); + } +#else + if ((socketFD = socket( AF_INET, SOCK_DGRAM, 0 )) < 0) + return 0; +#if defined(IPv6) && defined(AF_INET6) + socket6FD = socket( AF_INET6, SOCK_DGRAM, 0 ); +#endif +# ifdef SO_BROADCAST + soopts = 1; + if (setsockopt( socketFD, SOL_SOCKET, SO_BROADCAST, (char *)&soopts, + sizeof(soopts) ) < 0) + perror( "setsockopt" ); +# endif +#endif + + return 1; +} + +static void ATTR_NORETURN +chooseHost( int hid ) +{ + HostName *h; +#if defined(IPv6) && defined(AF_INET6) + char addr[64]; +#endif + + for (h = hostNamedb; h; h = h->next) + if ((int)h == hid) { + /* XXX error handling */ + GSet( &mstrtalk ); + if ((td->displayType & d_location) == dLocal) { + GSendInt( D_RemoteHost ); +#if defined(IPv6) && defined(AF_INET6) + switch (h->connectionType) { + case FamilyInternet6: + inet_ntop( AF_INET6, h->hostaddr.data, addr, sizeof(addr) ); + break; + default: /* FamilyInternet */ + inet_ntop( AF_INET, h->hostaddr.data, addr, sizeof(addr) ); + break; + } + GSendStr( addr ); +#else + GSendStr( inet_ntoa( *(struct in_addr *)h->hostaddr.data ) ); +#endif + CloseGreeter( FALSE ); + SessionExit( EX_REMOTE ); + } else { + GSendInt( D_ChooseHost ); + GSendArr( td->clientAddr.length, (char *)td->clientAddr.data ); + GSendInt( td->connectionType ); + GSendArr( h->hostaddr.length, (char *)h->hostaddr.data ); + CloseGreeter( FALSE ); + goto bout; + } + break; + } +/* LogError( "Internal error: chose unexisting host\n" ); */ + bout: + SessionExit( EX_NORMAL ); +} + +static void +directChooseHost( const char *name ) +{ + HostAddr *hosts = 0; + + if (!makeSockAddrs( name, &hosts )) + return; + GSendInt( G_Ch_Exit ); + /* XXX error handling */ + GSet( &mstrtalk ); + if ((td->displayType & d_location) == dLocal) { + GSendInt( D_RemoteHost ); + GSendStr( name ); + CloseGreeter( FALSE ); + SessionExit( EX_REMOTE ); + } else { + GSendInt( D_ChooseHost ); + GSendArr( td->clientAddr.length, (char *)td->clientAddr.data ); + GSendInt( td->connectionType ); + GSendArr( hosts->addrlen, (char *)hosts->addr ); + CloseGreeter( FALSE ); + SessionExit( EX_NORMAL ); + } +} + +#define PING_TRIES 3 + +int +DoChoose() +{ + HostName **hp, *h; + char *host, **hostp; + struct timeval *to, tnow, nextPing; + int pingTry, n, cmd; + FD_TYPE rfds; + static int xdmcpInited; + + OpenGreeter(); + GSendInt( G_Choose ); + switch (cmd = CtrlGreeterWait( TRUE )) { + case G_Ready: + break; + default: /* error */ + return cmd; + } + + if (!xdmcpInited) { + if (!initXDMCP()) + SessionExit( EX_UNMANAGE_DPY ); + xdmcpInited = 1; + } + if ((td->displayType & d_location) == dLocal) { + /* XXX the config reader should do the lookup already */ + for (hostp = td->chooserHosts; *hostp; hostp++) + if (!registerForPing( *hostp )) + LogError( "Unkown host %\"s specified for local chooser preload of display %s\n", *hostp, td->name ); + } else + ForEachChooserHost( &td->clientAddr, td->connectionType, + AddChooserHost, 0 ); + + GSendInt( 0 ); /* entering async mode signal */ + + reping: + for (h = hostNamedb; h; h = h->next) + h->alive = 0; + pingTry = 0; + goto pingen; + + for (;;) { + to = 0; + if (pingTry <= PING_TRIES) { + gettimeofday( &tnow, 0 ); + if (nextPing.tv_sec < tnow.tv_sec || + (nextPing.tv_sec == tnow.tv_sec && + nextPing.tv_usec < tnow.tv_usec)) { + if (pingTry < PING_TRIES) { + pingen: + pingTry++; + doPingHosts(); + gettimeofday( &tnow, 0 ); + nextPing = tnow; + nextPing.tv_sec++; + } else { + for (hp = &hostNamedb; *hp; ) + if (!(*hp)->alive) { + h = (*hp)->next; + disposeHostname( *hp ); + GSendInt( G_Ch_RemoveHost ); + GSendInt( (int)*hp ); /* just an id */ + *hp = h; + } else + hp = &(*hp)->next; + goto noto; + } + } + to = &tnow; + tnow.tv_sec = nextPing.tv_sec - tnow.tv_sec; + tnow.tv_usec = nextPing.tv_usec - tnow.tv_usec; + if (tnow.tv_usec < 0) { + tnow.tv_usec += 1000000; + tnow.tv_sec--; + } + } + noto: + FD_ZERO( &rfds ); + FD_SET( grtproc.pipe.rfd, &rfds ); + FD_SET( socketFD, &rfds ); +#if defined(IPv6) && defined(AF_INET6) + if (socket6FD >= 0) + FD_SET( socket6FD, &rfds ); +#endif + n = grtproc.pipe.rfd; + if (socketFD > n) + n = socketFD; +#if defined(IPv6) && defined(AF_INET6) + if (socket6FD > n) + n = socket6FD; +#endif + if (select( n + 1, &rfds, 0, 0, to ) > 0) { + if (FD_ISSET( grtproc.pipe.rfd, &rfds )) + switch (cmd = CtrlGreeterWait( FALSE )) { + case -1: + break; + case G_Ch_Refresh: + goto reping; + case G_Ch_RegisterHost: + host = GRecvStr(); + if (!registerForPing( host )) { + GSendInt( G_Ch_BadHost ); + GSendStr( host ); + } + free( host ); + goto reping; + case G_Ch_DirectChoice: + host = GRecvStr(); + directChooseHost( host ); + GSendInt( G_Ch_BadHost ); + GSendStr( host ); + free( host ); + break; + case G_Ready: + chooseHost( GRecvInt() ); + /* NOTREACHED */ + default: + emptyHostnames(); + emptyPingHosts(); + return cmd; + } + if (FD_ISSET( socketFD, &rfds )) + receivePacket( socketFD ); +#if defined(IPv6) && defined(AF_INET6) + if (socket6FD >= 0 && FD_ISSET( socket6FD, &rfds )) + receivePacket( socket6FD ); +#endif + } + } + +} + +#endif /* XDMCP */ |