summaryrefslogtreecommitdiffstats
path: root/tdm/backend/choose.c
diff options
context:
space:
mode:
Diffstat (limited to 'tdm/backend/choose.c')
-rw-r--r--tdm/backend/choose.c1051
1 files changed, 1051 insertions, 0 deletions
diff --git a/tdm/backend/choose.c b/tdm/backend/choose.c
new file mode 100644
index 000000000..ead841228
--- /dev/null
+++ b/tdm/backend/choose.c
@@ -0,0 +1,1051 @@
+/*
+
+Copyright 1990, 1998 The Open Group
+Copyright 2002-2004 Oswald Buddenhagen <ossi@kde.org>
+
+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 */