chooser.c revision 629baa8c
1/*
2 *
3Copyright 1990, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24 *
25 * Author:  Keith Packard, MIT X Consortium
26 */
27
28
29/*
30 * Chooser - display a menu of names and let the user select one
31 */
32
33/*
34 * Layout:
35 *
36 *  +--------------------------------------------------+
37 *  |             +------------------+                 |
38 *  |             |      Label       |                 |
39 *  |             +------------------+                 |
40 *  |    +-+--------------+                            |
41 *  |    |^| name-1       |                            |
42 *  |    ||| name-2       |                            |
43 *  |    |v| name-3       |                            |
44 *  |    | | name-4       |                            |
45 *  |    | | name-5       |                            |
46 *  |    | | name-6       |                            |
47 *  |    +----------------+                            |
48 *  |    cancel  accept  ping                          |
49 *  +--------------------------------------------------+
50 */
51
52#include    <X11/Intrinsic.h>
53#include    <X11/StringDefs.h>
54#include    <X11/Xatom.h>
55
56#include    <X11/Xaw/Paned.h>
57#include    <X11/Xaw/Label.h>
58#include    <X11/Xaw/Viewport.h>
59#include    <X11/Xaw/List.h>
60#include    <X11/Xaw/Box.h>
61#include    <X11/Xaw/Command.h>
62
63#include    "dm.h"
64
65#include    <X11/Xdmcp.h>
66
67#include    <sys/types.h>
68#include    <stdio.h>
69#include    <ctype.h>
70
71#ifdef USE_XINERAMA
72# include    <X11/extensions/Xinerama.h>
73#endif
74
75#if defined(SVR4)
76# include    <sys/sockio.h>
77#endif
78#if defined(SVR4) && defined(PowerMAX_OS)
79# include    <sys/stropts.h>
80#endif
81#if defined(SYSV) && defined(i386)
82# include    <sys/stream.h>
83# ifdef ISC
84#  include    <sys/sioctl.h>
85#  include    <sys/stropts.h>
86# endif
87#endif
88
89#include    "dm_socket.h"
90
91#include    <arpa/inet.h>
92
93#include    <sys/ioctl.h>
94#ifdef STREAMSCONN
95# ifdef WINTCP /* NCR with Wollongong TCP */
96#  include    <netinet/ip.h>
97# endif
98# include    <stropts.h>
99# include    <tiuser.h>
100# include    <netconfig.h>
101# include    <netdir.h>
102#endif
103
104#ifdef HAVE_SYS_PARAM_H
105#include <sys/param.h>
106# ifdef BSD
107#  if (BSD >= 199103)
108#   define VARIABLE_IFREQ
109#  endif
110# endif
111#endif
112
113#ifdef XKB
114# include <X11/extensions/XKBbells.h>
115#endif
116
117#define BROADCAST_HOSTNAME  "BROADCAST"
118
119#ifndef ishexdigit
120# define ishexdigit(c)	(isdigit(c) || ('a' <= (c) && (c) <= 'f'))
121#endif
122
123#ifdef hpux
124# include <sys/utsname.h>
125# ifdef HAS_IFREQ
126#  include <net/if.h>
127# endif
128#else
129# ifdef __convex__
130#  include <sync/queue.h>
131#  include <sync/sema.h>
132# endif
133# include <net/if.h>
134#endif /* hpux */
135
136#include    <netdb.h>
137#include    <X11/keysym.h>
138
139static int FromHex (char *s, char *d, int len);
140static int oldline;
141
142static Widget	    toplevel, label, viewport, paned, list, box, cancel, acceptit, ping;
143
144static void	CvtStringToARRAY8(
145    XrmValuePtr	args,
146    Cardinal	*num_args,
147    XrmValuePtr	fromVal,
148    XrmValuePtr	toVal);
149
150static struct _app_resources {
151    ARRAY8Ptr   xdmAddress;
152    ARRAY8Ptr	clientAddress;
153    int		connectionType;
154} app_resources;
155
156#define offset(field) XtOffsetOf(struct _app_resources, field)
157
158#define XtRARRAY8   "ARRAY8"
159
160static XtResource  resources[] = {
161    {"xdmAddress",	"XdmAddress",  XtRARRAY8,	sizeof (ARRAY8Ptr),
162	offset (xdmAddress),	    XtRString,	NULL },
163    {"clientAddress",	"ClientAddress",  XtRARRAY8,	sizeof (ARRAY8Ptr),
164	offset (clientAddress),	    XtRString,	NULL },
165    {"connectionType",	"ConnectionType",   XtRInt,	sizeof (int),
166	offset (connectionType),    XtRImmediate,	(XtPointer) 0 }
167};
168#undef offset
169
170static XrmOptionDescRec options[] = {
171    { "-xdmaddress",	"*xdmAddress",	    XrmoptionSepArg,	NULL },
172    { "-clientaddress",	"*clientAddress",   XrmoptionSepArg,	NULL },
173    { "-connectionType","*connectionType",  XrmoptionSepArg,	NULL },
174};
175
176typedef struct _hostAddr {
177    struct _hostAddr	*next;
178    struct sockaddr	*addr;
179    int			addrlen;
180    xdmOpCode		type;
181} HostAddr;
182
183static HostAddr    *hostAddrdb;
184
185typedef struct _hostName {
186    struct _hostName	*next;
187    char		*fullname;
188    int			willing;
189    ARRAY8		hostname, status;
190    CARD16		connectionType;
191    ARRAY8		hostaddr;
192} HostName;
193
194static HostName    *hostNamedb;
195
196static int  socketFD;
197#if defined(IPv6) && defined(AF_INET6)
198static int  socket6FD;
199#endif
200
201static int  pingTry;
202
203#define PING_INTERVAL	2000
204#define TRIES		3
205
206static XdmcpBuffer	directBuffer, broadcastBuffer;
207static XdmcpBuffer	buffer;
208
209#if ((defined(SVR4) && !defined(sun) && !defined(__sgi) && !defined(NCR)) || defined(ISC)) && defined(SIOCGIFCONF)
210
211/* Deal with different SIOCGIFCONF ioctl semantics on these OSs */
212
213static int
214ifioctl (int fd, int cmd, char *arg)
215{
216    struct strioctl ioc;
217    int ret;
218
219    bzero((char *) &ioc, sizeof(ioc));
220    ioc.ic_cmd = cmd;
221    ioc.ic_timout = 0;
222    if (cmd == SIOCGIFCONF)
223    {
224	ioc.ic_len = ((struct ifconf *) arg)->ifc_len;
225	ioc.ic_dp = ((struct ifconf *) arg)->ifc_buf;
226# ifdef ISC
227	/* SIOCGIFCONF is somewhat brain damaged on ISC. The argument
228	 * buffer must contain the ifconf structure as header. Ifc_req
229	 * is also not a pointer but a one element array of ifreq
230	 * structures. On return this array is extended by enough
231	 * ifreq fields to hold all interfaces. The return buffer length
232	 * is placed in the buffer header.
233	 */
234        ((struct ifconf *) ioc.ic_dp)->ifc_len =
235                                         ioc.ic_len - sizeof(struct ifconf);
236# endif
237    }
238    else
239    {
240	ioc.ic_len = sizeof(struct ifreq);
241	ioc.ic_dp = arg;
242    }
243    ret = ioctl(fd, I_STR, (char *) &ioc);
244    if (ret >= 0 && cmd == SIOCGIFCONF)
245# ifdef SVR4
246	((struct ifconf *) arg)->ifc_len = ioc.ic_len;
247# endif
248# ifdef ISC
249    {
250	((struct ifconf *) arg)->ifc_len =
251				 ((struct ifconf *)ioc.ic_dp)->ifc_len;
252	((struct ifconf *) arg)->ifc_buf =
253			(caddr_t)((struct ifconf *)ioc.ic_dp)->ifc_req;
254    }
255# endif
256    return(ret);
257}
258#else /* ((SVR4 && !sun && !NCR) || ISC) && SIOCGIFCONF */
259# define ifioctl ioctl
260#endif /* ((SVR4 && !sun) || ISC) && SIOCGIFCONF */
261
262
263/* ARGSUSED */
264static void
265PingHosts (XtPointer closure, XtIntervalId *id)
266{
267    HostAddr	*hosts;
268    int		 sfd = socketFD;
269
270    for (hosts = hostAddrdb; hosts; hosts = hosts->next)
271    {
272#if defined(IPv6) && defined(AF_INET6)
273	if ( ((struct sockaddr *) hosts->addr)->sa_family == AF_INET6 )
274	    sfd = socket6FD;
275	else
276	    sfd = socketFD;
277#endif
278	if (hosts->type == QUERY)
279	    XdmcpFlush (sfd, &directBuffer,
280			(XdmcpNetaddr) hosts->addr, hosts->addrlen);
281	else
282	    XdmcpFlush (sfd, &broadcastBuffer,
283			(XdmcpNetaddr) hosts->addr, hosts->addrlen);
284    }
285    if (++pingTry < TRIES)
286	XtAddTimeOut (PING_INTERVAL, PingHosts, (XtPointer) 0);
287}
288
289char	**NameTable;
290int	NameTableSize;
291
292static int
293HostnameCompare (const void *a, const void *b)
294{
295    return strcmp (*(char **)a, *(char **)b);
296}
297
298static void
299RebuildTable (int size)
300{
301    char	**newTable = NULL;
302    HostName	*names;
303    int		i;
304
305    if (size)
306    {
307	newTable = malloc (size * sizeof (char *));
308	if (!newTable)
309	    return;
310	for (names = hostNamedb, i = 0; names; names = names->next, i++)
311	    newTable[i] = names->fullname;
312	qsort (newTable, size, sizeof (char *), HostnameCompare);
313    }
314    XawListChange (list, newTable, size, 0, TRUE);
315    free (NameTable);
316    NameTable = newTable;
317    NameTableSize = size;
318}
319
320static int
321AddHostname (ARRAY8Ptr hostname, ARRAY8Ptr status, struct sockaddr *addr, int willing)
322{
323    HostName	*new, **names, *name;
324    ARRAY8	hostAddr = {0, NULL};
325    CARD16	connectionType;
326    int		fulllen;
327
328    switch (addr->sa_family)
329    {
330    case AF_INET:
331	hostAddr.data = (CARD8 *) &((struct sockaddr_in *) addr)->sin_addr;
332	hostAddr.length = 4;
333	connectionType = FamilyInternet;
334	break;
335#if defined(IPv6) && defined(AF_INET6)
336    case AF_INET6:
337	hostAddr.data = (CARD8 *) &((struct sockaddr_in6 *) addr)->sin6_addr;
338	hostAddr.length = 16;
339	connectionType = FamilyInternet6;
340	break;
341#endif
342    default:
343	hostAddr.data = (CARD8 *) "";
344	hostAddr.length = 0;
345	connectionType = FamilyLocal;
346	break;
347    }
348    for (names = &hostNamedb; *names; names = & (*names)->next)
349    {
350	name = *names;
351	if (connectionType == name->connectionType &&
352	    XdmcpARRAY8Equal (&hostAddr, &name->hostaddr))
353	{
354	    if (XdmcpARRAY8Equal (status, &name->status))
355	    {
356		return 0;
357	    }
358	    break;
359	}
360    }
361    if (!*names)
362    {
363	new = malloc (sizeof (HostName));
364	if (!new)
365	    return 0;
366	if (hostname->length)
367	{
368	    switch (addr->sa_family)
369	    {
370	    case AF_INET:
371#if defined(IPv6) && defined(AF_INET6)
372	    case AF_INET6:
373#endif
374		{
375		    struct hostent  *hostent;
376		    char	    *host;
377
378		    hostent = gethostbyaddr ((char *)hostAddr.data, hostAddr.length, addr->sa_family);
379		    if (hostent)
380		    {
381			XdmcpDisposeARRAY8 (hostname);
382			host = (char *)hostent->h_name;
383			XdmcpAllocARRAY8 (hostname, strlen (host));
384			memmove( hostname->data, host, hostname->length);
385		    }
386		}
387	    }
388	}
389	if (!XdmcpAllocARRAY8 (&new->hostaddr, hostAddr.length))
390	{
391	    free (new->fullname);
392	    free (new);
393	    return 0;
394	}
395	memmove( new->hostaddr.data, hostAddr.data, hostAddr.length);
396	new->connectionType = connectionType;
397	new->hostname = *hostname;
398
399	*names = new;
400	new->next = NULL;
401	NameTableSize++;
402    }
403    else
404    {
405	new = *names;
406	free (new->fullname);
407	XdmcpDisposeARRAY8 (&new->status);
408	XdmcpDisposeARRAY8 (hostname);
409    }
410    new->willing = willing;
411    new->status = *status;
412
413    hostname = &new->hostname;
414    fulllen = hostname->length;
415    if (fulllen < 30)
416	fulllen = 30;
417    fulllen += status->length + 10;
418    new->fullname = malloc (fulllen);
419    if (!new->fullname)
420    {
421	new->fullname = "Unknown";
422    }
423    else
424    {
425	snprintf(new->fullname, fulllen, "%-30.*s %*.*s",
426		 hostname->length, hostname->data,
427		 status->length, status->length, status->data);
428    }
429    RebuildTable (NameTableSize);
430    return 1;
431}
432
433static void
434DisposeHostname (HostName *host)
435{
436    XdmcpDisposeARRAY8 (&host->hostname);
437    XdmcpDisposeARRAY8 (&host->hostaddr);
438    XdmcpDisposeARRAY8 (&host->status);
439    free (host->fullname);
440    free (host);
441}
442
443#if 0
444static void
445RemoveHostname (HostName *host)
446{
447    HostName	**prev, *hosts;
448
449    prev = &hostNamedb;;
450    for (hosts = hostNamedb; hosts; hosts = hosts->next)
451    {
452	if (hosts == host)
453	    break;
454	prev = &hosts->next;
455    }
456    if (!hosts)
457	return;
458    *prev = host->next;
459    DisposeHostname (host);
460    NameTableSize--;
461    RebuildTable (NameTableSize);
462}
463#endif
464
465static void
466EmptyHostnames (void)
467{
468    HostName	*hosts, *next;
469
470    for (hosts = hostNamedb; hosts; hosts = next)
471    {
472	next = hosts->next;
473	DisposeHostname (hosts);
474    }
475    NameTableSize = 0;
476    hostNamedb = NULL;
477    RebuildTable (NameTableSize);
478}
479
480/* ARGSUSED */
481static void
482ReceivePacket (XtPointer closure, int *source, XtInputId *id)
483{
484    XdmcpHeader	    header;
485    ARRAY8	    authenticationName = {0, NULL};
486    ARRAY8	    hostname = {0, NULL};
487    ARRAY8	    status = {0, NULL};
488    int		    saveHostname = 0;
489#if defined(IPv6) && defined(AF_INET6)
490    struct sockaddr_storage addr;
491#else
492    struct sockaddr addr;
493#endif
494    int		    addrlen;
495    int		    sfd = * (int *) closure;
496
497    addrlen = sizeof (addr);
498    if (!XdmcpFill (sfd, &buffer, (XdmcpNetaddr) &addr, &addrlen))
499	return;
500    if (!XdmcpReadHeader (&buffer, &header))
501	return;
502    if (header.version != XDM_PROTOCOL_VERSION)
503	return;
504    switch (header.opcode) {
505    case WILLING:
506	if (XdmcpReadARRAY8 (&buffer, &authenticationName) &&
507	    XdmcpReadARRAY8 (&buffer, &hostname) &&
508	    XdmcpReadARRAY8 (&buffer, &status))
509	{
510	    if (header.length == 6 + authenticationName.length +
511		hostname.length + status.length)
512	    {
513		if (AddHostname (&hostname, &status, (struct sockaddr *) &addr,
514				 header.opcode == (int) WILLING))
515		    saveHostname = 1;
516	    }
517	}
518	XdmcpDisposeARRAY8 (&authenticationName);
519	break;
520    case UNWILLING:
521	if (XdmcpReadARRAY8 (&buffer, &hostname) &&
522	    XdmcpReadARRAY8 (&buffer, &status))
523	{
524	    if (header.length == 4 + hostname.length + status.length)
525	    {
526		if (AddHostname (&hostname, &status, (struct sockaddr *) &addr,
527				 header.opcode == (int) WILLING))
528		    saveHostname = 1;
529
530	    }
531	}
532	break;
533    default:
534	break;
535    }
536    if (!saveHostname)
537    {
538	XdmcpDisposeARRAY8 (&hostname);
539	XdmcpDisposeARRAY8 (&status);
540    }
541}
542
543static void
544RegisterHostaddr (struct sockaddr *addr, int len, xdmOpCode type)
545{
546    HostAddr		*host, **prev;
547
548    host = malloc (sizeof (HostAddr));
549    if (!host)
550	return;
551    host->addr = malloc (len);
552    if (!host->addr)
553    {
554	free (host);
555	return;
556    }
557    memmove( (char *) host->addr, (char *) addr, len);
558    host->addrlen = len;
559    host->type = type;
560    for (prev = &hostAddrdb; *prev; prev = &(*prev)->next)
561	;
562    *prev = host;
563    host->next = NULL;
564}
565
566/*
567 * Register the address for this host.
568 * Called with each of the names on the command line.
569 * The special name "BROADCAST" looks up all the broadcast
570 *  addresses on the local host.
571 */
572
573/* Handle variable length ifreq in BNR2 and later */
574#ifdef VARIABLE_IFREQ
575# define ifr_size(p) (sizeof (struct ifreq) + \
576		     (p->ifr_addr.sa_len > sizeof (p->ifr_addr) ? \
577		      p->ifr_addr.sa_len - sizeof (p->ifr_addr) : 0))
578#else
579# define ifr_size(p) (sizeof (struct ifreq))
580#endif
581
582static void
583RegisterHostname (char *name)
584{
585#if !defined(IPv6) || !defined(AF_INET6)
586    struct hostent	*hostent;
587#endif
588    struct sockaddr_in	in_addr;
589    struct ifconf	ifc;
590    register struct ifreq *ifr;
591    struct sockaddr	broad_addr;
592    char		buf[2048], *cp, *cplim;
593
594    if (!strcmp (name, BROADCAST_HOSTNAME))
595    {
596#ifdef WINTCP /* NCR with Wollongong TCP */
597    int                 ipfd;
598    struct ifconf       *ifcp;
599    struct strioctl     ioc;
600    int			n;
601
602	ifcp = (struct ifconf *)buf;
603	ifcp->ifc_buf = buf+4;
604	ifcp->ifc_len = sizeof (buf) - 4;
605
606	if ((ipfd=open( "/dev/ip", O_RDONLY )) < 0 )
607	    {
608	    t_error( "RegisterHostname() t_open(/dev/ip) failed" );
609	    return;
610	    }
611
612	ioc.ic_cmd = IPIOC_GETIFCONF;
613	ioc.ic_timout = 60;
614	ioc.ic_len = sizeof( buf );
615	ioc.ic_dp = (char *)ifcp;
616
617	if (ioctl (ipfd, (int) I_STR, (char *) &ioc) < 0)
618	    {
619	    perror( "RegisterHostname() ioctl(I_STR(IPIOC_GETIFCONF)) failed" );
620	    close( ipfd );
621	    return;
622	    }
623
624	for (ifr = ifcp->ifc_req, n = ifcp->ifc_len / sizeof (struct ifreq);
625	    --n >= 0;
626	    ifr++)
627#else /* WINTCP */
628	ifc.ifc_len = sizeof (buf);
629	ifc.ifc_buf = buf;
630	if (ifioctl (socketFD, (int) SIOCGIFCONF, (char *) &ifc) < 0)
631	    return;
632
633# ifdef ISC
634#  define IFC_IFC_REQ (struct ifreq *) ifc.ifc_buf
635# else
636#  define IFC_IFC_REQ ifc.ifc_req
637# endif
638
639	cplim = (char *) IFC_IFC_REQ + ifc.ifc_len;
640
641	for (cp = (char *) IFC_IFC_REQ; cp < cplim; cp += ifr_size (ifr))
642#endif /* WINTCP */
643	{
644#ifndef WINTCP
645	    ifr = (struct ifreq *) cp;
646#endif
647	    if (ifr->ifr_addr.sa_family != AF_INET)
648		continue;
649
650	    broad_addr = ifr->ifr_addr;
651	    ((struct sockaddr_in *) &broad_addr)->sin_addr.s_addr =
652		htonl (INADDR_BROADCAST);
653#ifdef SIOCGIFBRDADDR
654	    {
655		struct ifreq    broad_req;
656
657		broad_req = *ifr;
658# ifdef WINTCP /* NCR with Wollongong TCP */
659		ioc.ic_cmd = IPIOC_GETIFFLAGS;
660		ioc.ic_timout = 0;
661		ioc.ic_len = sizeof( broad_req );
662		ioc.ic_dp = (char *)&broad_req;
663
664		if (ioctl (ipfd, I_STR, (char *) &ioc) != -1 &&
665# else /* WINTCP */
666		if (ifioctl (socketFD, SIOCGIFFLAGS, (char *) &broad_req) != -1 &&
667# endif /* WINTCP */
668		    (broad_req.ifr_flags & IFF_BROADCAST) &&
669		    (broad_req.ifr_flags & IFF_UP)
670		    )
671		{
672		    broad_req = *ifr;
673# ifdef WINTCP /* NCR with Wollongong TCP */
674		    ioc.ic_cmd = IPIOC_GETIFBRDADDR;
675		    ioc.ic_timout = 0;
676		    ioc.ic_len = sizeof( broad_req );
677		    ioc.ic_dp = (char *)&broad_req;
678
679		    if (ioctl (ipfd, I_STR, (char *) &ioc) != -1)
680# else /* WINTCP */
681		    if (ifioctl (socketFD, SIOCGIFBRDADDR, &broad_req) != -1)
682# endif /* WINTCP */
683			broad_addr = broad_req.ifr_addr;
684		    else
685			continue;
686		}
687		else
688		    continue;
689	    }
690#endif
691	    in_addr = *((struct sockaddr_in *) &broad_addr);
692	    in_addr.sin_port = htons (XDM_UDP_PORT);
693#ifdef BSD44SOCKETS
694	    in_addr.sin_len = sizeof(in_addr);
695#endif
696	    RegisterHostaddr ((struct sockaddr *)&in_addr, sizeof (in_addr),
697			      BROADCAST_QUERY);
698	}
699    }
700    else
701    {
702	/* address as hex string, e.g., "12180022" (deprecated) */
703	if (strlen(name) == 8 &&
704	    FromHex(name, (char *)&in_addr.sin_addr, strlen(name)) == 0)
705	{
706	    in_addr.sin_family = AF_INET;
707	    in_addr.sin_port = htons (XDM_UDP_PORT);
708#ifdef BSD44SOCKETS
709	    in_addr.sin_len = sizeof(in_addr);
710#endif
711	    RegisterHostaddr ((struct sockaddr *)&in_addr, sizeof (in_addr),
712				QUERY);
713	}
714#if defined(IPv6) && defined(AF_INET6)
715	else {
716	    char sport[8];
717	    struct addrinfo *ai, *nai, hints;
718	    bzero(&hints,sizeof(hints));
719	    hints.ai_socktype = SOCK_DGRAM;
720	    snprintf(sport, sizeof(sport), "%d", XDM_UDP_PORT);
721	    if (getaddrinfo(name, sport, &hints, &ai) == 0) {
722		for (nai = ai ; nai != NULL ; nai = nai->ai_next) {
723		    if ((nai->ai_family == AF_INET) ||
724		        (nai->ai_family == AF_INET6)) {
725			if (((nai->ai_family == AF_INET) &&
726			  IN_MULTICAST(((struct sockaddr_in *) nai->ai_addr)
727			    ->sin_addr.s_addr))
728			  || ((nai->ai_family == AF_INET6) &&
729			    IN6_IS_ADDR_MULTICAST(
730				&((struct sockaddr_in6 *) nai->ai_addr)
731				  ->sin6_addr)))
732			{
733			    RegisterHostaddr(nai->ai_addr, nai->ai_addrlen,
734			      BROADCAST_QUERY);
735			} else {
736			    RegisterHostaddr(nai->ai_addr, nai->ai_addrlen,
737			      QUERY);
738			}
739		    }
740		}
741		freeaddrinfo(ai);
742	    }
743	}
744#else
745	/* Per RFC 1123, check first for IP address in dotted-decimal form */
746	else if ((in_addr.sin_addr.s_addr = inet_addr(name)) != -1)
747	    in_addr.sin_family = AF_INET;
748	else
749	{
750	    hostent = gethostbyname (name);
751	    if (!hostent)
752		return;
753	    if (hostent->h_addrtype != AF_INET || hostent->h_length != 4)
754		return;
755	    in_addr.sin_family = hostent->h_addrtype;
756	    memmove( &in_addr.sin_addr, hostent->h_addr, 4);
757	}
758	in_addr.sin_port = htons (XDM_UDP_PORT);
759# ifdef BSD44SOCKETS
760	in_addr.sin_len = sizeof(in_addr);
761# endif
762	RegisterHostaddr ((struct sockaddr *)&in_addr, sizeof (in_addr),
763			  QUERY);
764#endif /* IPv6 */
765    }
766}
767
768static ARRAYofARRAY8	AuthenticationNames;
769
770#if 0
771static void
772RegisterAuthenticationName (char *name, int namelen)
773{
774    ARRAY8Ptr	authName;
775    if (!XdmcpReallocARRAYofARRAY8 (&AuthenticationNames,
776				    AuthenticationNames.length + 1))
777	return;
778    authName = &AuthenticationNames.data[AuthenticationNames.length-1];
779    if (!XdmcpAllocARRAY8 (authName, namelen))
780	return;
781    memmove( authName->data, name, namelen);
782}
783#endif
784
785static int
786InitXDMCP (char **argv)
787{
788    int	soopts = 1;
789    XdmcpHeader	header;
790    int	i;
791
792    header.version = XDM_PROTOCOL_VERSION;
793    header.opcode = (CARD16) BROADCAST_QUERY;
794    header.length = 1;
795    for (i = 0; i < (int)AuthenticationNames.length; i++)
796	header.length += 2 + AuthenticationNames.data[i].length;
797    XdmcpWriteHeader (&broadcastBuffer, &header);
798    XdmcpWriteARRAYofARRAY8 (&broadcastBuffer, &AuthenticationNames);
799
800    header.version = XDM_PROTOCOL_VERSION;
801    header.opcode = (CARD16) QUERY;
802    header.length = 1;
803    for (i = 0; i < (int)AuthenticationNames.length; i++)
804	header.length += 2 + AuthenticationNames.data[i].length;
805    XdmcpWriteHeader (&directBuffer, &header);
806    XdmcpWriteARRAYofARRAY8 (&directBuffer, &AuthenticationNames);
807#if defined(STREAMSCONN)
808    if ((socketFD = t_open ("/dev/udp", O_RDWR, 0)) < 0)
809	return 0;
810
811    if (t_bind( socketFD, NULL, NULL ) < 0)
812	{
813	t_close(socketFD);
814	return 0;
815	}
816
817    /*
818     * This part of the code looks contrived. It will actually fit in nicely
819     * when the CLTS part of Xtrans is implemented.
820     */
821    {
822    struct netconfig *nconf;
823
824    if( (nconf=getnetconfigent("udp")) == NULL )
825	{
826	t_unbind(socketFD);
827	t_close(socketFD);
828	return 0;
829	}
830
831    if( netdir_options(nconf, ND_SET_BROADCAST, socketFD, NULL) )
832	{
833	freenetconfigent(nconf);
834	t_unbind(socketFD);
835	t_close(socketFD);
836	return 0;
837	}
838
839    freenetconfigent(nconf);
840    }
841#else
842    if ((socketFD = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
843	return 0;
844# if defined(IPv6) && defined(AF_INET6)
845    socket6FD = socket (AF_INET6, SOCK_DGRAM, 0);
846# endif
847#endif
848#ifndef STREAMSCONN
849# ifdef SO_BROADCAST
850    soopts = 1;
851    if (setsockopt (socketFD, SOL_SOCKET, SO_BROADCAST, (char *)&soopts, sizeof (soopts)) < 0)
852	perror ("setsockopt");
853# endif
854#endif
855
856    XtAddInput (socketFD, (XtPointer) XtInputReadMask, ReceivePacket,
857		(XtPointer) &socketFD);
858#if defined(IPv6) && defined(AF_INET6)
859    if (socket6FD != -1)
860    XtAddInput (socket6FD, (XtPointer) XtInputReadMask, ReceivePacket,
861		(XtPointer) &socket6FD);
862#endif
863    while (*argv)
864    {
865	RegisterHostname (*argv);
866	++argv;
867    }
868    pingTry = 0;
869    PingHosts ((XtPointer)NULL, (XtIntervalId *)NULL);
870    return 1;
871}
872
873static void
874Choose (HostName *h)
875{
876    if (app_resources.xdmAddress)
877    {
878	struct sockaddr_in  in_addr;
879#if defined(IPv6) && defined(AF_INET6)
880	struct sockaddr_in6 in6_addr;
881#endif
882	struct sockaddr	*addr = NULL;
883	int		family;
884	int		len = 0;
885	int		fd;
886	char		buf[1024];
887	XdmcpBuffer	buffer;
888	char		*xdm;
889#if defined(STREAMSCONN)
890        struct  t_call  call, rcv;
891#endif
892
893	xdm = (char *) app_resources.xdmAddress->data;
894	family = (xdm[0] << 8) + xdm[1];
895	switch (family) {
896	case AF_INET:
897#ifdef BSD44SOCKETS
898	    in_addr.sin_len = sizeof(in_addr);
899#endif
900	    in_addr.sin_family = family;
901	    memmove( &in_addr.sin_port, xdm + 2, 2);
902	    memmove( &in_addr.sin_addr, xdm + 4, 4);
903	    addr = (struct sockaddr *) &in_addr;
904	    len = sizeof (in_addr);
905	    break;
906#if defined(IPv6) && defined(AF_INET6)
907	case AF_INET6:
908	    bzero(&in6_addr, sizeof(in6_addr));
909# ifdef SIN6_LEN
910	    in6_addr.sin6_len = sizeof(in6_addr);
911# endif
912	    in6_addr.sin6_family = family;
913	    memmove( &in6_addr.sin6_port, xdm + 2, 2);
914	    memmove( &in6_addr.sin6_addr, xdm + 4, 16);
915	    addr = (struct sockaddr *) &in6_addr;
916	    len = sizeof (in6_addr);
917	    break;
918#endif
919	}
920#if defined(STREAMSCONN)
921	if ((fd = t_open ("/dev/tcp", O_RDWR, NULL)) == -1)
922	{
923	    fprintf (stderr, "Cannot create response endpoint\n");
924	    fflush(stderr);
925	    exit (REMANAGE_DISPLAY);
926	}
927	if (t_bind (fd, NULL, NULL) == -1)
928	{
929	    fprintf (stderr, "Cannot bind response endpoint\n");
930	    fflush(stderr);
931	    t_close (fd);
932	    exit (REMANAGE_DISPLAY);
933	}
934	call.addr.buf=(char *)addr;
935	call.addr.len=len;
936	call.addr.maxlen=len;
937	call.opt.len=0;
938	call.opt.maxlen=0;
939	call.udata.len=0;
940	call.udata.maxlen=0;
941	if (t_connect (fd, &call, NULL) == -1)
942	{
943	    t_error ("Cannot connect to xdm\n");
944	    fflush(stderr);
945	    t_unbind (fd);
946	    t_close (fd);
947	    exit (REMANAGE_DISPLAY);
948	}
949#else
950	if ((fd = socket (family, SOCK_STREAM, 0)) == -1)
951	{
952	    fprintf (stderr, "Cannot create response socket\n");
953	    exit (REMANAGE_DISPLAY);
954	}
955	if (connect (fd, addr, len) == -1)
956	{
957	    fprintf (stderr, "Cannot connect to xdm\n");
958	    exit (REMANAGE_DISPLAY);
959	}
960#endif
961	buffer.data = (BYTE *) buf;
962	buffer.size = sizeof (buf);
963	buffer.pointer = 0;
964	buffer.count = 0;
965	XdmcpWriteARRAY8 (&buffer, app_resources.clientAddress);
966	XdmcpWriteCARD16 (&buffer, (CARD16) app_resources.connectionType);
967	XdmcpWriteARRAY8 (&buffer, &h->hostaddr);
968#if defined(STREAMSCONN)
969	if( t_snd (fd, (char *)buffer.data, buffer.pointer, 0) < 0 )
970	{
971	    fprintf (stderr, "Cannot send to xdm\n");
972	    fflush(stderr);
973	    t_unbind (fd);
974	    t_close (fd);
975	    exit (REMANAGE_DISPLAY);
976	}
977	sleep(5);	/* Hack because sometimes the connection gets
978			   closed before the data arrives on the other end. */
979	t_snddis (fd,NULL);
980	t_unbind (fd);
981	t_close (fd);
982#else
983	write (fd, (char *)buffer.data, buffer.pointer);
984	close (fd);
985#endif
986    }
987    else
988    {
989	int i;
990
991	printf ("%u\n", h->connectionType);
992	for (i = 0; i < (int)h->hostaddr.length; i++)
993	    printf ("%u%s", h->hostaddr.data[i],
994		    i == h->hostaddr.length - 1 ? "\n" : " ");
995    }
996}
997
998/*
999  next_line returns the next line in a list
1000  across the list end.
1001  (0, 1, 2, 3, 0, 1, 2, 3 ....)
1002*/
1003
1004static int
1005next_line(unsigned int current, unsigned int size, int event)
1006{
1007  switch(event) {
1008    case Button5:
1009      return (current + 1) % size;
1010    case Button4:
1011      return (current + size - 1) % size;
1012    case XK_Down:
1013      return (current + 1) % size;
1014    case XK_Up:
1015      return (current + size - 1) % size;
1016  }
1017  return -1;
1018}
1019
1020/*
1021  Hostselect selects a given chooser line.
1022  Returns 1 when host is willing and 0 if not
1023*/
1024
1025static int
1026Hostselect (int line)
1027{
1028  XawListReturnStruct	*r;
1029  HostName		*h;
1030
1031  /* Assume the next host is willing */
1032  XawListHighlight (list,line);
1033  r = XawListShowCurrent (list);
1034  /* copied from DoCheckWilling */
1035  for (h = hostNamedb; h; h = h->next)
1036  {
1037    if (!strcmp (r->string, h->fullname))
1038    {
1039      if (!h->willing)
1040	XawListUnhighlight (list);
1041      else
1042      {
1043	/* Scroll viewport to make sure new selection is visible */
1044	Arg		size[2];
1045	Dimension	height, portheight;
1046	Position	y;
1047	int		lineheight, liney;
1048
1049	XtSetArg (size[0], XtNheight, &height);
1050	XtSetArg (size[1], XtNy, &y);
1051	XtGetValues (list, size, (Cardinal) 2);
1052
1053	XtSetArg (size[0], XtNheight, &portheight);
1054	XtGetValues (viewport, size, (Cardinal) 1);
1055
1056	lineheight = height / NameTableSize;
1057	liney = lineheight * line;
1058
1059	if ((y + liney) < 0) {
1060	    XawViewportSetCoordinates(viewport, 0, liney);
1061	} else if ((y + liney + lineheight) > portheight) {
1062	    XawViewportSetCoordinates(viewport, 0,
1063				      (liney + lineheight) - portheight);
1064	}
1065
1066	XtFree((char *) r);
1067	return 1;
1068      }
1069    }
1070  }
1071  XtFree((char *) r);
1072  return 0;
1073}
1074
1075/*
1076  Selectfirst selects the first willing host
1077  in the chooser list (so you can select a host without
1078  presence of mouse, but with pressing space or return,
1079  or XK_Down / XK_Up
1080*/
1081
1082static void
1083Selectfirst (void)
1084{
1085  int line;
1086
1087  for (line = 0; line < NameTableSize; line++)
1088  {
1089    if (Hostselect(line))
1090      return;
1091  }
1092  return;
1093}
1094
1095/*
1096  Storeold stores the selected line into global int oldentry
1097*/
1098
1099static void
1100Storeold (Widget w, XEvent *event, String *params, Cardinal *num_params)
1101{
1102  XawListReturnStruct	*r = XawListShowCurrent(list);
1103
1104  oldline = r->list_index;
1105  XtFree((char *) r);
1106}
1107
1108/*
1109  Setold restores the global int oldentry
1110  when you try to select a host with your mouse
1111  who is unwilling as follows:
1112  <Btn1Down>:     Store() Set() CheckWilling() Setold() \n\
1113*/
1114
1115static void
1116Setold (Widget w, XEvent *event, String *params, Cardinal *num_params)
1117{
1118
1119  if ( (XawListShowCurrent(list))->list_index == XAW_LIST_NONE && oldline != XAW_LIST_NONE)
1120    XawListHighlight (list, oldline);
1121}
1122
1123/*
1124  HostCycle tries to select the next willing host across
1125  the list end and will stop at the first found willing host
1126  or after trying all entries.
1127*/
1128
1129static void
1130HostCycle(unsigned int line, unsigned int size, KeySym keysym)
1131{
1132  unsigned int newline = line;
1133  /* Do it only once around the chooser list, either direction */
1134  while ( (newline = next_line(newline,size,keysym)) != line && newline != -1)
1135  {
1136    if (Hostselect(newline))
1137      return;
1138  }
1139  /* No other willing host could be found, stay at the old one*/
1140  XawListHighlight (list, line);
1141  return;
1142}
1143
1144/*
1145  Switch_Key handles XK_Up and XK_Down
1146  and highlights an appropriate line in the chooser list.
1147*/
1148
1149static void
1150Switch_Key (Widget w, XEvent *event, String *params, Cardinal *num_params)
1151{
1152  char strbuf[128];
1153  KeySym keysym = 0;
1154  static XComposeStatus compose_status = {NULL, 0};
1155  XawListReturnStruct	*r;
1156
1157  XLookupString (&event->xkey, strbuf, sizeof (strbuf),
1158                           &keysym, &compose_status);
1159
1160  if (keysym != XK_Up && keysym != XK_Down)
1161    return;
1162
1163  r = XawListShowCurrent (list);
1164
1165  if (r->list_index == XAW_LIST_NONE)
1166    Selectfirst();
1167  else
1168    HostCycle(r->list_index,NameTableSize,keysym);
1169
1170  XtFree((char *) r);
1171  return;
1172}
1173
1174
1175
1176/*
1177  Switch_Btn handles ScrollWheel Forward (Button5) and Backward
1178  (Button4) and highlights an appropriate line in the chooser list.
1179*/
1180
1181static void
1182Switch_Btn (Widget w, XEvent *event, String *params, Cardinal *num_params)
1183{
1184    XawListReturnStruct	*r;
1185    r = XawListShowCurrent (list);
1186
1187    if (r->list_index == XAW_LIST_NONE)
1188      Selectfirst();
1189    else
1190      HostCycle(r->list_index,NameTableSize,event->xbutton.button);
1191
1192    XtFree((char *) r);
1193    return;
1194}
1195
1196
1197/* ARGSUSED */
1198static void
1199DoAccept (Widget w, XEvent *event, String *params, Cardinal *num_params)
1200{
1201    XawListReturnStruct	*r;
1202    HostName		*h;
1203
1204    r = XawListShowCurrent (list);
1205    if (r->list_index == XAW_LIST_NONE)
1206#ifdef XKB
1207	XkbStdBell(XtDisplay(toplevel),XtWindow(w),0,XkbBI_MinorError);
1208#else
1209	XBell (XtDisplay (toplevel), 0);
1210#endif
1211    else
1212    {
1213	for (h = hostNamedb; h; h = h->next)
1214	    if (!strcmp (r->string, h->fullname))
1215	    {
1216		Choose (h);
1217	    }
1218	exit (OBEYSESS_DISPLAY);
1219    }
1220    XtFree((char *) r);
1221}
1222
1223/* ARGSUSED */
1224static void
1225DoCheckWilling (Widget w, XEvent *event, String *params, Cardinal *num_params)
1226{
1227    XawListReturnStruct	*r;
1228    HostName		*h;
1229
1230    r = XawListShowCurrent (list);
1231    if (r->list_index != XAW_LIST_NONE) {
1232	for (h = hostNamedb; h; h = h->next)
1233	    if (!strcmp (r->string, h->fullname))
1234		if (!h->willing)
1235		    XawListUnhighlight (list);
1236    }
1237    XtFree((char *) r);
1238}
1239
1240/* ARGSUSED */
1241static void
1242DoCancel (Widget w, XEvent *event, String *params, Cardinal *num_params)
1243{
1244    exit (OBEYSESS_DISPLAY);
1245}
1246
1247/* ARGSUSED */
1248static void
1249DoPing (Widget w, XEvent *event, String *params, Cardinal *num_params)
1250{
1251    EmptyHostnames ();
1252    pingTry = 0;
1253    PingHosts ((XtPointer)NULL, (XtIntervalId *)NULL);
1254}
1255
1256static XtActionsRec app_actions[] = {
1257    { "Accept",	      DoAccept },
1258    { "Cancel",	      DoCancel },
1259    { "CheckWilling", DoCheckWilling },
1260    { "Ping",	      DoPing },
1261    { "KeySwitch",    Switch_Key },
1262    { "BtnSwitch",    Switch_Btn },
1263    { "Store",	      Storeold },
1264    { "Setold",	      Setold },
1265};
1266
1267int
1268main (int argc, char **argv)
1269{
1270    Arg		position[3];
1271    Dimension   width, height;
1272    Position	x, y;
1273#ifdef USE_XINERAMA
1274    XineramaScreenInfo *screens;
1275    int                 s_num;
1276#endif
1277
1278
1279    toplevel = XtInitialize (argv[0], "Chooser", options, XtNumber(options), &argc, argv);
1280
1281    XtAddConverter(XtRString, XtRARRAY8, CvtStringToARRAY8, NULL, 0);
1282
1283    XtGetApplicationResources (toplevel, (XtPointer) &app_resources, resources,
1284			       XtNumber (resources), NULL, (Cardinal) 0);
1285
1286    XtAddActions (app_actions, XtNumber (app_actions));
1287    paned = XtCreateManagedWidget ("paned", panedWidgetClass, toplevel, NULL, 0);
1288    label = XtCreateManagedWidget ("label", labelWidgetClass, paned, NULL, 0);
1289    viewport = XtCreateManagedWidget ("viewport", viewportWidgetClass, paned, NULL, 0);
1290    list = XtCreateManagedWidget ("list", listWidgetClass, viewport, NULL, 0);
1291    box = XtCreateManagedWidget ("box", boxWidgetClass, paned, NULL, 0);
1292    cancel = XtCreateManagedWidget ("cancel", commandWidgetClass, box, NULL, 0);
1293    acceptit = XtCreateManagedWidget ("accept", commandWidgetClass, box, NULL, 0);
1294    ping = XtCreateManagedWidget ("ping", commandWidgetClass, box, NULL, 0);
1295
1296    /*
1297     * center ourselves on the screen
1298     */
1299    XtSetMappedWhenManaged(toplevel, FALSE);
1300    XtRealizeWidget (toplevel);
1301
1302    XtSetArg (position[0], XtNwidth, &width);
1303    XtSetArg (position[1], XtNheight, &height);
1304    XtGetValues (toplevel, position, (Cardinal) 2);
1305#ifdef USE_XINERAMA
1306    if (
1307	XineramaIsActive(XtDisplay(toplevel)) &&
1308	(screens = XineramaQueryScreens(XtDisplay(toplevel), &s_num)) != NULL
1309       )
1310    {
1311	x = (Position)(screens[0].x_org + (screens[0].width - width) / 2);
1312	y = (Position)(screens[0].y_org + (screens[0].height - height) / 3);
1313
1314	XFree(screens);
1315    }
1316    else
1317#endif
1318    {
1319	x = (Position)(WidthOfScreen (XtScreen (toplevel)) - width) / 2;
1320	y = (Position)(HeightOfScreen (XtScreen (toplevel)) - height) / 3;
1321    }
1322    XtSetArg (position[0], XtNx, x);
1323    XtSetArg (position[1], XtNy, y);
1324    XtSetValues (toplevel, position, (Cardinal) 2);
1325
1326    /*
1327     * Run
1328     */
1329    XtMapWidget(toplevel);
1330    InitXDMCP (argv + 1);
1331    XtMainLoop ();
1332    exit(0);
1333    /*NOTREACHED*/
1334}
1335
1336/* Converts the hex string s of length len into the byte array d.
1337   Returns 0 if s was a legal hex string, 1 otherwise.
1338   */
1339static int
1340FromHex (char *s, char *d, int len)
1341{
1342    int	t;
1343    int ret = len&1;		/* odd-length hex strings are illegal */
1344    while (len >= 2)
1345    {
1346#define HexChar(c)  ('0' <= (c) && (c) <= '9' ? (c) - '0' : (c) - 'a' + 10)
1347
1348	if (!ishexdigit(*s))
1349	    ret = 1;
1350	t = HexChar (*s) << 4;
1351	s++;
1352	if (!ishexdigit(*s))
1353	    ret = 1;
1354	t += HexChar (*s);
1355	s++;
1356	*d++ = t;
1357	len -= 2;
1358    }
1359    return ret;
1360}
1361
1362/*ARGSUSED*/
1363static void
1364CvtStringToARRAY8 (XrmValuePtr args, Cardinal *num_args, XrmValuePtr fromVal, XrmValuePtr toVal)
1365{
1366    static ARRAY8Ptr	dest;
1367    char	*s;
1368    int		len;
1369
1370    dest = (ARRAY8Ptr) XtMalloc (sizeof (ARRAY8));
1371    len = fromVal->size;
1372    s = (char *) fromVal->addr;
1373    if (!XdmcpAllocARRAY8 (dest, len >> 1))
1374	XtStringConversionWarning ((char *) fromVal->addr, XtRARRAY8);
1375    else
1376    {
1377	FromHex (s, (char *) dest->data, len);
1378    }
1379    toVal->addr = (caddr_t) &dest;
1380    toVal->size = sizeof (ARRAY8Ptr);
1381}
1382