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 * choose.c
31 *
32 * xdm interface to chooser program
33 */
34
35#include "dm.h"
36#include "dm_error.h"
37
38#ifdef XDMCP
39
40# include <X11/X.h>
41# include <sys/types.h>
42
43# include "dm_socket.h"
44# include <arpa/inet.h>
45# include <sys/un.h>
46
47# include <ctype.h>
48# include <errno.h>
49
50# ifdef HAVE_SETPROCTITLE
51#  include <unistd.h>
52# endif
53
54# include <time.h>
55# define Time_t time_t
56
57static int
58FormatBytes (
59    unsigned char *data,
60    int	    length,
61    char    *buf,
62    int	    buflen)
63{
64    int	    i;
65    static char	    HexChars[] = "0123456789abcdef";
66
67    if (buflen < length * 2 + 1)
68	return 0;
69    for (i = 0; i < length; i++)
70    {
71	*buf++ = HexChars[(data[i] >> 4) & 0xf];
72	*buf++ = HexChars[(data[i]) & 0xf];
73    }
74    *buf++ = '\0';
75    return 1;
76}
77
78static int
79FormatARRAY8 (
80    ARRAY8Ptr	a,
81    char	*buf,
82    int		buflen)
83{
84    return FormatBytes (a->data, a->length, buf, buflen);
85}
86
87/* Converts an Internet address in ARRAY8 format to a string in
88   familiar dotted address notation, e.g., "18.24.0.11"
89   Returns 1 if successful, 0 if not.
90   */
91static int
92ARRAY8ToDottedDecimal (
93    ARRAY8Ptr	a,
94    char	*buf,
95    int		buflen)
96{
97    int outlen;
98    if (a->length != 4)
99	return 0;
100    outlen = snprintf(buf, buflen, "%d.%d.%d.%d",
101		      a->data[0], a->data[1], a->data[2], a->data[3]);
102    if (outlen >= buflen) {
103	return 0;
104    }
105    return 1;
106}
107
108typedef struct _IndirectUsers {
109    struct _IndirectUsers   *next;
110    ARRAY8	client;
111    CARD16	connectionType;
112} IndirectUsersRec, *IndirectUsersPtr;
113
114static IndirectUsersPtr	indirectUsers;
115
116int
117RememberIndirectClient (
118    ARRAY8Ptr	clientAddress,
119    CARD16	connectionType)
120{
121    IndirectUsersPtr	i;
122
123    for (i = indirectUsers; i; i = i->next)
124	if (XdmcpARRAY8Equal (clientAddress, &i->client) &&
125	    connectionType == i->connectionType)
126	    return 1;
127    i = malloc (sizeof (IndirectUsersRec));
128    if (!i)
129    {
130	LogOutOfMem ("RememberIndirectClient\n");
131	return 0;
132    }
133    if (!XdmcpCopyARRAY8 (clientAddress, &i->client))
134    {
135	free (i);
136	return 0;
137    }
138    i->connectionType = connectionType;
139    i->next = indirectUsers;
140    indirectUsers = i;
141    return 1;
142}
143
144void
145ForgetIndirectClient (
146    ARRAY8Ptr	clientAddress,
147    CARD16	connectionType)
148{
149    IndirectUsersPtr	i, prev;
150
151    prev = NULL;
152    for (i = indirectUsers; i; i = i->next)
153    {
154	if (XdmcpARRAY8Equal (clientAddress, &i->client) &&
155	    connectionType == i->connectionType)
156	{
157	    if (prev)
158		prev->next = i->next;
159	    else
160		indirectUsers = i->next;
161	    XdmcpDisposeARRAY8 (&i->client);
162	    free (i);
163	    break;
164	}
165	prev = i;
166    }
167}
168
169int
170IsIndirectClient (
171    ARRAY8Ptr	clientAddress,
172    CARD16	connectionType)
173{
174    IndirectUsersPtr	i;
175
176    for (i = indirectUsers; i; i = i->next)
177	if (XdmcpARRAY8Equal (clientAddress, &i->client) &&
178	    connectionType == i->connectionType)
179	    return 1;
180    return 0;
181}
182
183static int
184FormatChooserArgument (char *buf, int len)
185{
186    unsigned char   addr_buf[1024];
187    int		    addr_len = sizeof (addr_buf);
188    unsigned char   result_buf[1024];
189    int		    result_len = 0;
190    int		    netfamily;
191
192    if (GetChooserAddr ((char *)addr_buf, &addr_len) == -1)
193    {
194	LogError ("Cannot get return address for chooser socket\n");
195	Debug ("Cannot get chooser socket address\n");
196	return 0;
197    }
198    netfamily = NetaddrFamily((XdmcpNetaddr)addr_buf);
199    switch (netfamily) {
200    case AF_INET:
201# ifdef IPv6
202    case AF_INET6:
203# endif
204	{
205	    char *port;
206	    int portlen;
207	    ARRAY8Ptr localAddress = getLocalAddress ();
208
209# ifdef IPv6
210	    if (localAddress->length == 16)
211		netfamily = AF_INET6;
212	    else
213		netfamily = AF_INET;
214# endif
215
216	    port = NetaddrPort((XdmcpNetaddr)addr_buf, &portlen);
217	    if (port == NULL) {
218		LogError ("Cannot get port for chooser socket\n");
219		return 0;
220	    }
221	    result_buf[0] = netfamily >> 8;
222	    result_buf[1] = netfamily & 0xFF;
223	    result_buf[2] = port[0];
224	    result_buf[3] = port[1];
225	    memcpy(result_buf+4, localAddress->data, localAddress->length);
226	    result_len = 4 + localAddress->length;
227	}
228	break;
229    default:
230	Debug ("Chooser family %d isn't known\n", netfamily);
231	return 0;
232    }
233
234    return FormatBytes (result_buf, result_len, buf, len);
235}
236
237typedef struct _Choices {
238    struct _Choices *next;
239    ARRAY8	    client;
240    CARD16	    connectionType;
241    ARRAY8	    choice;
242    Time_t	    time;
243} ChoiceRec, *ChoicePtr;
244
245static ChoicePtr   choices;
246
247ARRAY8Ptr
248IndirectChoice (
249    ARRAY8Ptr	clientAddress,
250    CARD16	connectionType)
251{
252    ChoicePtr	c, next, prev;
253    Time_t	now;
254
255    now = time ((Time_t*)0);
256    prev = NULL;
257    for (c = choices; c; c = next)
258    {
259	next = c->next;
260	Debug ("Choice checking timeout: %ld >? %d\n",
261	    (long)(now - c->time), choiceTimeout);
262	if (now - c->time > (Time_t)choiceTimeout)
263	{
264	    Debug ("Timeout choice %ld > %d\n",
265		(long)(now - c->time), choiceTimeout);
266	    if (prev)
267		prev->next = next;
268	    else
269		choices = next;
270	    XdmcpDisposeARRAY8 (&c->client);
271	    XdmcpDisposeARRAY8 (&c->choice);
272	    free (c);
273	}
274	else
275	{
276	    if (XdmcpARRAY8Equal (clientAddress, &c->client) &&
277		connectionType == c->connectionType)
278		return &c->choice;
279	    prev = c;
280	}
281    }
282    return NULL;
283}
284
285static int
286RegisterIndirectChoice (
287    ARRAY8Ptr	clientAddress,
288    CARD16      connectionType,
289    ARRAY8Ptr	choice)
290{
291    ChoicePtr	c;
292    int		insert;
293
294    Debug ("Got indirect choice back\n");
295    for (c = choices; c; c = c->next) {
296	if (XdmcpARRAY8Equal (clientAddress, &c->client) &&
297	    connectionType == c->connectionType) {
298	    break;
299	}
300    }
301    insert = 0;
302    if (!c)
303    {
304	c = malloc (sizeof (ChoiceRec));
305	insert = 1;
306	if (!c)
307	    return 0;
308	c->connectionType = connectionType;
309	if (!XdmcpCopyARRAY8 (clientAddress, &c->client))
310	{
311	    free (c);
312	    return 0;
313	}
314    }
315    else
316    {
317	XdmcpDisposeARRAY8 (&c->choice);
318    }
319    if (!XdmcpCopyARRAY8 (choice, &c->choice))
320    {
321	XdmcpDisposeARRAY8 (&c->client);
322	free (c);
323	return 0;
324    }
325    if (insert)
326    {
327	c->next = choices;
328	choices = c;
329    }
330    c->time = time ((Time_t *) 0);
331    return 1;
332}
333
334# ifdef notdef
335static
336RemoveIndirectChoice (clientAddress, connectionType)
337    ARRAY8Ptr	clientAddress;
338    CARD16	connectionType;
339{
340    ChoicePtr	c, prev;
341
342    prev = 0;
343    for (c = choices; c; c = c->next)
344    {
345	if (XdmcpARRAY8Equal (clientAddress, &c->client) &&
346	    connectionType == c->connectionType)
347	{
348	    if (prev)
349		prev->next = c->next;
350	    else
351		choices = c->next;
352	    XdmcpDisposeARRAY8 (&c->client);
353	    XdmcpDisposeARRAY8 (&c->choice);
354	    free (c);
355	    return;
356	}
357	prev = c;
358    }
359}
360# endif
361
362/*ARGSUSED*/
363static void
364AddChooserHost (
365    CARD16	connectionType,
366    ARRAY8Ptr	addr,
367    char	*closure)
368{
369    char	***argp;
370    char	hostbuf[1024];
371
372    argp = (char ***) closure;
373    if (addr->length == strlen ("BROADCAST") &&
374	!strncmp ((char *)addr->data, "BROADCAST", addr->length))
375    {
376	*argp = parseArgs (*argp, "BROADCAST");
377    }
378# ifdef IPv6
379    else if ( (addr->length == 16) &&
380      (inet_ntop(AF_INET6, addr->data, hostbuf, sizeof(hostbuf))))
381    {
382	*argp = parseArgs (*argp, hostbuf);
383    }
384# endif
385    else if (ARRAY8ToDottedDecimal (addr, hostbuf, sizeof (hostbuf)))
386    {
387	*argp = parseArgs (*argp, hostbuf);
388    }
389}
390
391void
392ProcessChooserSocket (int fd)
393{
394    int client_fd;
395    char	buf[1024];
396    int		len;
397    XdmcpBuffer	buffer;
398    ARRAY8	clientAddress = {0, NULL};
399    CARD16	connectionType;
400    ARRAY8	choice = {0, NULL};
401
402    Debug ("Process chooser socket\n");
403    len = sizeof (buf);
404    client_fd = accept (fd, (struct sockaddr *)buf, (void *)&len);
405    if (client_fd == -1)
406    {
407	LogError ("Cannot accept chooser connection\n");
408	return;
409    }
410    Debug ("Accepted %d\n", client_fd);
411
412    len = read (client_fd, buf, sizeof (buf));
413    Debug ("Read returns %d\n", len);
414    if (len > 0)
415    {
416	buffer.data = (BYTE *) buf;
417	buffer.size = sizeof (buf);
418	buffer.count = len;
419	buffer.pointer = 0;
420	if (XdmcpReadARRAY8 (&buffer, &clientAddress)) {
421	    if (XdmcpReadCARD16 (&buffer, &connectionType)) {
422		if (XdmcpReadARRAY8 (&buffer, &choice)) {
423		    Debug ("Read from chooser successfully\n");
424		    RegisterIndirectChoice (&clientAddress, connectionType, &choice);
425		    XdmcpDisposeARRAY8 (&choice);
426		} else {
427		    LogError ("Invalid choice response length %d\n", len);
428		}
429	    } else {
430		LogError ("Invalid choice response length %d\n", len);
431	    }
432	    XdmcpDisposeARRAY8 (&clientAddress);
433	} else {
434	    LogError ("Invalid choice response length %d\n", len);
435	}
436    }
437    else
438    {
439	LogError ("Choice response read error: %s\n", _SysErrorMsg(errno));
440    }
441
442    close (client_fd);
443}
444
445void
446RunChooser (struct display *d)
447{
448    char    **args;
449    char    buf[1024];
450    char    **env;
451
452    Debug ("RunChooser %s\n", d->name);
453# ifndef HAVE_SETPROCTITLE
454    SetTitle (d->name, "chooser", (char *) 0);
455# else
456    setproctitle("chooser %s", d->name);
457# endif
458    LoadXloginResources (d);
459    args = parseArgs ((char **) 0, d->chooser);
460    strcpy (buf, "-xdmaddress ");
461    if (FormatChooserArgument (buf + strlen (buf), sizeof (buf) - strlen (buf)))
462	args = parseArgs (args, buf);
463    strcpy (buf, "-clientaddress ");
464    if (FormatARRAY8 (&d->clientAddr, buf + strlen (buf), sizeof (buf) - strlen (buf)))
465	args = parseArgs (args, buf);
466    snprintf (buf, sizeof(buf), "-connectionType %d", d->connectionType);
467    args = parseArgs (args, buf);
468    ForEachChooserHost (&d->clientAddr, d->connectionType, AddChooserHost,
469			(char *) &args);
470    env = systemEnv (d, (char *) 0, (char *) 0);
471    Debug ("Running %s\n", args[0]);
472    execute (args, env);
473    Debug ("Couldn't run %s\n", args[0]);
474    LogError ("Cannot execute %s\n", args[0]);
475    exit (REMANAGE_DISPLAY);
476}
477
478#endif /* XDMCP */
479