Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtlogin / xdmcp.c
1 /* $TOG: xdmcp.c /main/5 1998/04/06 13:36:04 mgreess $ */
2 /* (c) Copyright 1997 The Open Group */
3 /*                                                                      *
4  * (c) Copyright 1993, 1994 Hewlett-Packard Company                     *
5  * (c) Copyright 1993, 1994 International Business Machines Corp.       *
6  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.                      *
7  * (c) Copyright 1993, 1994 Novell, Inc.                                *
8  */
9
10 /*
11  * @DEC_COPYRIGHT@
12  */
13 /*
14  * HISTORY
15  * $Log$
16  * Revision 1.1.2.3  1995/06/06  20:25:54  Chris_Beute
17  *      Code snapshot merge from March 15 and SIA changes
18  *      [1995/05/31  20:17:42  Chris_Beute]
19  *
20  * Revision 1.1.2.2  1995/04/21  13:05:47  Peter_Derr
21  *      dtlogin auth key fixes from deltacde
22  *      [1995/04/14  18:03:44  Peter_Derr]
23  * 
24  *      Merge in dtlogin changes to WaitForSomething() to support command
25  *      line console login.
26  *      [1995/04/14  17:40:05  Peter_Derr]
27  * 
28  *      Use R6 xdm code to handle XDMCP
29  *      [1995/04/10  19:24:11  Peter_Derr]
30  * 
31  * $EndLog$
32  */
33 /*
34
35 Copyright (c) 1988  X Consortium
36
37 Permission is hereby granted, free of charge, to any person obtaining
38 a copy of this software and associated documentation files (the
39 "Software"), to deal in the Software without restriction, including
40 without limitation the rights to use, copy, modify, merge, publish,
41 distribute, sublicense, and/or sell copies of the Software, and to
42 permit persons to whom the Software is furnished to do so, subject to
43 the following conditions:
44
45 The above copyright notice and this permission notice shall be included
46 in all copies or substantial portions of the Software.
47
48 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
49 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
50 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
51 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
52 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
53 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
54 OTHER DEALINGS IN THE SOFTWARE.
55
56 Except as contained in this notice, the name of the X Consortium shall
57 not be used in advertising or otherwise to promote the sale, use or
58 other dealings in this Software without prior written authorization
59 from the X Consortium.
60
61 */
62
63 /*
64  * xdm - display manager daemon
65  * Author:  Keith Packard, MIT X Consortium
66  *
67  * xdmcp.c - Support for XDMCP
68  */
69
70 # include "dm.h"
71
72 # include       <X11/X.h>
73 # include       <X11/Xfuncs.h>
74 # include       <sys/types.h>
75 # include       <ctype.h>
76
77 #include        <sys/socket.h>
78 #include        <netinet/in.h>
79 #include        <sys/un.h>
80 #include        <netdb.h>
81
82 #ifdef X_NOT_STDC_ENV
83 #define Time_t long
84 extern Time_t time ();
85 #else
86 #include <time.h>
87 #define Time_t time_t
88 #endif
89
90 #define getString(name,len)     ((name = malloc (len + 1)) ? 1 : 0)
91
92 /*
93  * interface to policy routines
94  */
95
96 extern ARRAY8Ptr        ChooseAuthentication ();
97 extern int              SelectConnectionTypeIndex ();
98
99 int     xdmcpFd = -1;
100 int     chooserFd = -1;
101
102 FD_TYPE WellKnownSocketsMask;
103 int     WellKnownSocketsMax;
104
105 #define pS(s)   ((s) ? ((char *) (s)) : "empty string")
106
107 void DestroyWellKnownSockets ()
108 {
109     if (xdmcpFd != -1)
110     {
111         close (xdmcpFd);
112         xdmcpFd = -1;
113     }
114     if (chooserFd != -1)
115     {
116         close (chooserFd);
117         chooserFd = -1;
118     }
119 }
120
121 AnyWellKnownSockets ()
122 {
123     return xdmcpFd != -1 || chooserFd != -1;
124 }
125
126 static XdmcpBuffer      buffer;
127
128 /*ARGSUSED*/
129 static int
130 sendForward (connectionType, address, closure)
131     CARD16      connectionType;
132     ARRAY8Ptr   address;
133     char        *closure;
134 {
135 #ifdef AF_INET
136     struct sockaddr_in      in_addr;
137 #endif
138 #ifdef AF_DECnet
139 #endif
140     struct sockaddr         *addr;
141     int                     addrlen;
142
143     switch (connectionType)
144     {
145 #ifdef AF_INET
146     case FamilyInternet:
147         addr = (struct sockaddr *) &in_addr;
148         bzero ((char *) &in_addr, sizeof (in_addr));
149 #ifdef BSD44SOCKETS
150         in_addr.sin_len = sizeof(in_addr);
151 #endif
152         in_addr.sin_family = AF_INET;
153         in_addr.sin_port = htons ((short) XDM_UDP_PORT);
154         if (address->length != 4)
155             return 0;
156         memmove( (char *) &in_addr.sin_addr, address->data, address->length);
157         addrlen = sizeof (struct sockaddr_in);
158         break;
159 #endif
160 #ifdef AF_DECnet
161     case FamilyDECnet:
162 #endif
163     default:
164         return 0;
165     }
166     XdmcpFlush (xdmcpFd, &buffer, addr, addrlen);
167     return 0;
168 }
169
170 extern char *NetaddrAddress();
171 extern char *NetaddrPort();
172
173 static void
174 ClientAddress (from, addr, port, type)
175     struct sockaddr *from;
176     ARRAY8Ptr       addr, port; /* return */
177     CARD16          *type;      /* return */
178 {
179     int length, family;
180     char *data;
181
182     data = NetaddrPort(from, &length);
183     XdmcpAllocARRAY8 (port, length);
184     memmove( port->data, data, length);
185     port->length = length;
186
187     family = ConvertAddr((char *)from, &length, &data);
188
189     XdmcpAllocARRAY8 (addr, length);
190     memmove( addr->data, data, length);
191     addr->length = length;
192
193     *type = family;
194 }
195
196 static void
197 all_query_respond (from, fromlen, authenticationNames, type)
198     struct sockaddr     *from;
199     int                 fromlen;
200     ARRAYofARRAY8Ptr    authenticationNames;
201     xdmOpCode           type;
202 {
203     ARRAY8Ptr   authenticationName;
204     ARRAY8      status;
205     ARRAY8      addr;
206     CARD16      connectionType;
207     int         family;
208     int         length;
209
210     family = ConvertAddr((char *)from, &length, (char **) &(addr.data));
211
212     addr.length = length;       /* convert int to short */
213     Debug ("all_query_respond: conntype=%d, addr=%lx, len=%d\n",
214            family, *(addr.data), addr.length);
215     if (family < 0)
216         return;
217     connectionType = family;
218
219     if (type == INDIRECT_QUERY)
220         RememberIndirectClient (&addr, connectionType);
221     else
222         ForgetIndirectClient (&addr, connectionType);
223
224     authenticationName = ChooseAuthentication (authenticationNames);
225     if (Willing (&addr, connectionType, authenticationName, &status, type))
226         send_willing (from, fromlen, authenticationName, &status);
227     else
228         if (type == QUERY)
229             send_unwilling (from, fromlen, authenticationName, &status);
230     XdmcpDisposeARRAY8 (&status);
231 }
232
233 static void
234 indirect_respond (from, fromlen, length)
235     struct sockaddr *from;
236     int             fromlen;
237     int             length;
238 {
239     ARRAYofARRAY8   queryAuthenticationNames;
240     ARRAY8          clientAddress;
241     ARRAY8          clientPort;
242     CARD16          connectionType;
243     int             expectedLen;
244     int             i;
245     XdmcpHeader     header;
246     int             localHostAsWell;
247     
248     Debug ("Indirect respond %d\n", length);
249     if (!XdmcpReadARRAYofARRAY8 (&buffer, &queryAuthenticationNames))
250         return;
251     expectedLen = 1;
252     for (i = 0; i < (int)queryAuthenticationNames.length; i++)
253         expectedLen += 2 + queryAuthenticationNames.data[i].length;
254     if (length == expectedLen)
255     {
256         ClientAddress (from, &clientAddress, &clientPort, &connectionType);
257         /*
258          * set up the forward query packet
259          */
260         header.version = XDM_PROTOCOL_VERSION;
261         header.opcode = (CARD16) FORWARD_QUERY;
262         header.length = 0;
263         header.length += 2 + clientAddress.length;
264         header.length += 2 + clientPort.length;
265         header.length += 1;
266         for (i = 0; i < (int)queryAuthenticationNames.length; i++)
267             header.length += 2 + queryAuthenticationNames.data[i].length;
268         XdmcpWriteHeader (&buffer, &header);
269         XdmcpWriteARRAY8 (&buffer, &clientAddress);
270         XdmcpWriteARRAY8 (&buffer, &clientPort);
271         XdmcpWriteARRAYofARRAY8 (&buffer, &queryAuthenticationNames);
272
273         localHostAsWell = ForEachMatchingIndirectHost (&clientAddress, connectionType, sendForward, (char *) 0);
274         
275         XdmcpDisposeARRAY8 (&clientAddress);
276         XdmcpDisposeARRAY8 (&clientPort);
277         if (localHostAsWell)
278             all_query_respond (from, fromlen, &queryAuthenticationNames,
279                            INDIRECT_QUERY);
280     }
281     else
282     {
283         Debug ("Indirect length error got %d expect %d\n", length, expectedLen);
284     }
285     XdmcpDisposeARRAYofARRAY8 (&queryAuthenticationNames);
286 }
287
288 static void
289 ProcessRequestSocket ()
290 {
291     XdmcpHeader         header;
292     struct sockaddr_in  addr;
293     int                 addrlen = sizeof addr;
294
295     Debug ("ProcessRequestSocket\n");
296     bzero ((char *) &addr, sizeof (addr));
297     if (!XdmcpFill (xdmcpFd, &buffer, &addr, &addrlen)) {
298         Debug ("XdmcpFill failed\n");
299         return;
300     }
301     if (!XdmcpReadHeader (&buffer, &header)) {
302         Debug ("XdmcpReadHeader failed\n");
303         return;
304     }
305     if (header.version != XDM_PROTOCOL_VERSION) {
306         Debug ("XDMCP header version read was %d, expected %d\n",
307                header.version, XDM_PROTOCOL_VERSION);
308         return;
309     }
310     Debug ("header: %d %d %d\n", header.version, header.opcode, header.length);
311     switch (header.opcode)
312     {
313     case BROADCAST_QUERY:
314         broadcast_respond (&addr, addrlen, header.length);
315         break;
316     case QUERY:
317         query_respond (&addr, addrlen, header.length);
318         break;
319     case INDIRECT_QUERY:
320         indirect_respond (&addr, addrlen, header.length);
321         break;
322     case FORWARD_QUERY:
323         forward_respond (&addr, addrlen, header.length);
324         break;
325     case REQUEST:
326         request_respond (&addr, addrlen, header.length);
327         break;
328     case MANAGE:
329         manage (&addr, addrlen, header.length);
330         break;
331     case KEEPALIVE:
332         send_alive (&addr, addrlen, header.length);
333         break;
334     }
335 }
336
337 /*
338  * dtlogin changes to WaitForSomething () merged in to support command line
339  * login.
340  */
341 void WaitForSomething ()
342 {
343     FD_TYPE     reads;
344     struct timeval      timeout, *ptimeout;
345     int nready;
346     extern int Rescan, ChildReady, wakeupTime;
347
348     Debug ("WaitForSomething\n");
349     if (AnyWellKnownSockets () && !ChildReady || wakeupTime > 0 ) {
350         reads = WellKnownSocketsMask;
351
352         if (wakeupTime >= 0 ) {
353             timeout.tv_sec  = wakeupTime;
354             timeout.tv_usec = 10;
355             ptimeout = &timeout;
356             Debug("Setting timer on select() for %d seconds.\n", wakeupTime);
357         }
358         else
359             ptimeout = NULL;
360
361 #ifdef __hpux
362         nready = select (WellKnownSocketsMax + 1, (int *) &reads, 0, 0, ptimeout);
363 #else
364         nready = select (WellKnownSocketsMax + 1, &reads, 0, 0, ptimeout);
365 #endif
366         Debug ("select returns %d.  Rescan: %d  ChildReady: %d\n",
367                 nready, Rescan, ChildReady);
368         if (nready > 0)
369         {
370             if (xdmcpFd >= 0 && FD_ISSET (xdmcpFd, &reads))
371                 ProcessRequestSocket ();
372             if (chooserFd >= 0 && FD_ISSET (chooserFd, &reads))
373                 ProcessChooserSocket (chooserFd);
374         }
375         if (ChildReady)
376         {
377             WaitForChild ();
378         }
379         else
380             StartDisplays();
381     } else
382         WaitForChild ();
383 }
384
385 /*
386  * respond to a request on the UDP socket.
387  */
388
389 static ARRAY8   Hostname;
390
391 void
392 registerHostname (name, namelen)
393     char    *name;
394     int     namelen;
395 {
396     int i;
397
398     if (!XdmcpReallocARRAY8 (&Hostname, namelen))
399         return;
400     for (i = 0; i < namelen; i++)
401         Hostname.data[i] = name[i];
402 }
403
404 static void
405 direct_query_respond (from, fromlen, length, type)
406     struct sockaddr *from;
407     int             fromlen;
408     int             length;
409     xdmOpCode       type;
410 {
411     ARRAYofARRAY8   queryAuthenticationNames;
412     int             expectedLen;
413     int             i;
414     
415     if (!XdmcpReadARRAYofARRAY8 (&buffer, &queryAuthenticationNames))
416         return;
417     expectedLen = 1;
418     for (i = 0; i < (int)queryAuthenticationNames.length; i++)
419         expectedLen += 2 + queryAuthenticationNames.data[i].length;
420     if (length == expectedLen)
421         all_query_respond (from, fromlen, &queryAuthenticationNames, type);
422     XdmcpDisposeARRAYofARRAY8 (&queryAuthenticationNames);
423 }
424
425 query_respond (from, fromlen, length)
426     struct sockaddr *from;
427     int             fromlen;
428     int             length;
429 {
430     Debug ("Query respond %d\n", length);
431     direct_query_respond (from, fromlen, length, QUERY);
432 }
433
434 broadcast_respond (from, fromlen, length)
435     struct sockaddr *from;
436     int             fromlen;
437     int             length;
438 {
439     direct_query_respond (from, fromlen, length, BROADCAST_QUERY);
440 }
441
442 /* computes an X display name */
443
444 char *
445 NetworkAddressToName(connectionType, connectionAddress, displayNumber)
446 #if NeedWidePrototypes
447     int connectionType;
448 #else
449     CARD16 connectionType;
450 #endif
451     ARRAY8Ptr   connectionAddress;
452 #if NeedWidePrototypes
453     int displayNumber;
454 #else
455     CARD16 displayNumber;
456 #endif
457 {
458     switch (connectionType)
459     {
460     case FamilyInternet:
461         {
462             CARD8               *data;
463             struct hostent      *hostent;
464             char                *name;
465             char                *localhost, *localHostname();
466
467             data = connectionAddress->data;
468             hostent = gethostbyaddr ((char *)data,
469                                      connectionAddress->length, AF_INET);
470
471             localhost = localHostname ();
472
473             /* 
474              * protect against bogus host names 
475              */
476             if (hostent && hostent->h_name && hostent->h_name[0]
477                         && (hostent->h_name[0] != '.'))
478             {
479                 if (!strcmp (localhost, hostent->h_name))
480                 {
481                     if (!getString (name, 10))
482                         return 0;
483                     sprintf (name, ":%d", displayNumber);
484                 }
485                 else
486                 {
487                     if (removeDomainname)
488                     {
489                         char    *localDot, *remoteDot;
490     
491                         /* check for a common domain name.  This
492                          * could reduce names by recognising common
493                          * super-domain names as well, but I don't think
494                          * this is as useful, and will confuse more
495                          * people
496                          */
497                         if ((localDot = strchr(localhost, '.')) &&
498                             (remoteDot = strchr(hostent->h_name, '.')))
499                         {
500                             /* smash the name in place; it won't
501                              * be needed later.
502                              */
503                             if (!strcmp (localDot+1, remoteDot+1))
504                                 *remoteDot = '\0';
505                         }
506                     }
507
508                     if (!getString (name, strlen (hostent->h_name) + 10))
509                         return 0;
510                     sprintf (name, "%s:%d", hostent->h_name, displayNumber);
511                 }
512             }
513             else
514             {
515                 if (!getString (name, 25))
516                     return 0;
517                 sprintf(name, "%d.%d.%d.%d:%d",
518                         data[0], data[1], data[2], data[3], displayNumber);
519             }
520             return name;
521         }
522 #ifdef DNET
523     case FamilyDECnet:
524         return NULL;
525 #endif /* DNET */
526     default:
527         return NULL;
528     }
529 }
530
531 /*ARGSUSED*/
532 forward_respond (from, fromlen, length)
533     struct sockaddr     *from;
534     int                 fromlen;
535     int                 length;
536 {
537     ARRAY8          clientAddress;
538     ARRAY8          clientPort;
539     ARRAYofARRAY8   authenticationNames;
540     struct sockaddr *client;
541     int             clientlen;
542     int             expectedLen;
543     int             i;
544     
545     Debug ("Forward respond %d\n", length);
546     clientAddress.length = 0;
547     clientAddress.data = 0;
548     clientPort.length = 0;
549     clientPort.data = 0;
550     authenticationNames.length = 0;
551     authenticationNames.data = 0;
552     if (XdmcpReadARRAY8 (&buffer, &clientAddress) &&
553         XdmcpReadARRAY8 (&buffer, &clientPort) &&
554         XdmcpReadARRAYofARRAY8 (&buffer, &authenticationNames))
555     {
556         expectedLen = 0;
557         expectedLen += 2 + clientAddress.length;
558         expectedLen += 2 + clientPort.length;
559         expectedLen += 1;           /* authenticationNames */
560         for (i = 0; i < (int)authenticationNames.length; i++)
561             expectedLen += 2 + authenticationNames.data[i].length;
562         if (length == expectedLen)
563         {
564             int j;
565
566             j = 0;
567             for (i = 0; i < (int)clientPort.length; i++)
568                 j = j * 256 + clientPort.data[i];
569             Debug ("Forward client address (port %d)", j);
570             for (i = 0; i < (int)clientAddress.length; i++)
571                 Debug (" %d", clientAddress.data[i]);
572             Debug ("\n");
573             switch (from->sa_family)
574             {
575 #ifdef AF_INET
576             case AF_INET:
577                 {
578                     struct sockaddr_in  in_addr;
579
580                     if (clientAddress.length != 4 ||
581                         clientPort.length != 2)
582                     {
583                         goto badAddress;
584                     }
585                     bzero ((char *) &in_addr, sizeof (in_addr));
586 #ifdef BSD44SOCKETS
587                     in_addr.sin_len = sizeof(in_addr);
588 #endif
589                     in_addr.sin_family = AF_INET;
590                     memmove( &in_addr.sin_addr, clientAddress.data, 4);
591                     memmove( (char *) &in_addr.sin_port, clientPort.data, 2);
592                     client = (struct sockaddr *) &in_addr;
593                     clientlen = sizeof (in_addr);
594                 }
595                 break;
596 #endif
597 #ifdef AF_UNIX
598             case AF_UNIX:
599                 {
600                     struct sockaddr_un  un_addr;
601
602                     if (clientAddress.length >= sizeof (un_addr.sun_path))
603                         goto badAddress;
604                     bzero ((char *) &un_addr, sizeof (un_addr));
605                     un_addr.sun_family = AF_UNIX;
606                     memmove( un_addr.sun_path, clientAddress.data, clientAddress.length);
607                     un_addr.sun_path[clientAddress.length] = '\0';
608                     client = (struct sockaddr *) &un_addr;
609 #ifdef BSD44SOCKETS
610                     un_addr.sun_len = strlen(un_addr.sun_path);
611                     clientlen = SUN_LEN(&un_addr);
612 #else
613                     clientlen = sizeof (un_addr);
614 #endif
615                 }
616                 break;
617 #endif
618 #ifdef AF_CHAOS
619             case AF_CHAOS:
620                 goto badAddress;
621 #endif
622 #ifdef AF_DECnet
623             case AF_DECnet:
624                 goto badAddress;
625 #endif
626             }
627             all_query_respond (client, clientlen, &authenticationNames,
628                                FORWARD_QUERY);
629         }
630         else
631         {
632             Debug ("Forward length error got %d expect %d\n", length, expectedLen);
633         }
634     }
635 badAddress:
636     XdmcpDisposeARRAY8 (&clientAddress);
637     XdmcpDisposeARRAY8 (&clientPort);
638     XdmcpDisposeARRAYofARRAY8 (&authenticationNames);
639 }
640
641 send_willing (from, fromlen, authenticationName, status)
642     struct sockaddr *from;
643     int             fromlen;
644     ARRAY8Ptr       authenticationName;
645     ARRAY8Ptr       status;
646 {
647     XdmcpHeader header;
648
649     Debug ("Send willing %*.*s %*.*s\n", authenticationName->length,
650                                          authenticationName->length,
651                                          pS(authenticationName->data),
652                                          status->length,
653                                          status->length,
654                                          pS(status->data));
655     header.version = XDM_PROTOCOL_VERSION;
656     header.opcode = (CARD16) WILLING;
657     header.length = 6 + authenticationName->length +
658                     Hostname.length + status->length;
659     XdmcpWriteHeader (&buffer, &header);
660     XdmcpWriteARRAY8 (&buffer, authenticationName);
661     XdmcpWriteARRAY8 (&buffer, &Hostname);
662     XdmcpWriteARRAY8 (&buffer, status);
663     XdmcpFlush (xdmcpFd, &buffer, from, fromlen);
664 }
665
666 send_unwilling (from, fromlen, authenticationName, status)
667     struct sockaddr *from;
668     int             fromlen;
669     ARRAY8Ptr       authenticationName;
670     ARRAY8Ptr       status;
671 {
672     XdmcpHeader header;
673
674     Debug ("Send unwilling %*.*s %*.*s\n", authenticationName->length,
675                                          authenticationName->length,
676                                          pS(authenticationName->data),
677                                          status->length,
678                                          status->length,
679                                          pS(status->data));
680     header.version = XDM_PROTOCOL_VERSION;
681     header.opcode = (CARD16) UNWILLING;
682     header.length = 4 + Hostname.length + status->length;
683     XdmcpWriteHeader (&buffer, &header);
684     XdmcpWriteARRAY8 (&buffer, &Hostname);
685     XdmcpWriteARRAY8 (&buffer, status);
686     XdmcpFlush (xdmcpFd, &buffer, from, fromlen);
687 }
688
689 static unsigned long    globalSessionID;
690
691 #define NextSessionID()    (++globalSessionID)
692
693 void init_session_id()
694 {
695     /* Set randomly so we are unlikely to reuse id's from a previous
696      * incarnation so we don't say "Alive" to those displays.
697      * Start with low digits 0 to make debugging easier.
698      */
699     globalSessionID = (time((Time_t)0)&0x7fff) * 16000;
700 }
701     
702 static ARRAY8 outOfMemory = { (CARD16) 13, (CARD8Ptr) "Out of memory" };
703 static ARRAY8 noValidAddr = { (CARD16) 16, (CARD8Ptr) "No valid address" };
704 static ARRAY8 noValidAuth = { (CARD16) 22, (CARD8Ptr) "No valid authorization" };
705 static ARRAY8 noAuthentic = { (CARD16) 29, (CARD8Ptr) "XDM has no authentication key" };
706
707 request_respond (from, fromlen, length)
708     struct sockaddr *from;
709     int             fromlen;
710     int             length;
711 {
712     CARD16          displayNumber;
713     ARRAY16         connectionTypes;
714     ARRAYofARRAY8   connectionAddresses;
715     ARRAY8          authenticationName;
716     ARRAY8          authenticationData;
717     ARRAYofARRAY8   authorizationNames;
718     ARRAY8          manufacturerDisplayID;
719     ARRAY8Ptr       reason;
720     int             expectlen;
721     int             i, j;
722     struct protoDisplay  *pdpy;
723     ARRAY8          authorizationName, authorizationData;
724     ARRAY8Ptr       connectionAddress;
725
726     Debug ("Request respond %d\n", length);
727     connectionTypes.data = 0;
728     connectionAddresses.data = 0;
729     authenticationName.data = 0;
730     authenticationData.data = 0;
731     authorizationNames.data = 0;
732     authorizationName.length = 0;
733     authorizationData.length = 0;
734     manufacturerDisplayID.data = 0;
735     if (XdmcpReadCARD16 (&buffer, &displayNumber) &&
736         XdmcpReadARRAY16 (&buffer, &connectionTypes) &&
737         XdmcpReadARRAYofARRAY8 (&buffer, &connectionAddresses) &&
738         XdmcpReadARRAY8 (&buffer, &authenticationName) &&
739         XdmcpReadARRAY8 (&buffer, &authenticationData) &&
740         XdmcpReadARRAYofARRAY8 (&buffer, &authorizationNames) &&
741         XdmcpReadARRAY8 (&buffer, &manufacturerDisplayID))
742     {
743         expectlen = 0;
744         expectlen += 2;                             /* displayNumber */
745         expectlen += 1 + 2*connectionTypes.length;  /* connectionTypes */
746         expectlen += 1;                             /* connectionAddresses */
747         for (i = 0; i < (int)connectionAddresses.length; i++)
748             expectlen += 2 + connectionAddresses.data[i].length;
749         expectlen += 2 + authenticationName.length; /* authenticationName */
750         expectlen += 2 + authenticationData.length; /* authenticationData */
751         expectlen += 1;                             /* authoriationNames */
752         for (i = 0; i < (int)authorizationNames.length; i++)
753             expectlen += 2 + authorizationNames.data[i].length;
754         expectlen += 2 + manufacturerDisplayID.length;  /* displayID */
755         if (expectlen != length)
756         {
757             Debug ("Request length error got %d expect %d\n", length, expectlen);
758             goto abort;
759         }
760         if (connectionTypes.length == 0 ||
761             connectionAddresses.length != connectionTypes.length)
762         {
763             reason = &noValidAddr;
764             pdpy = 0;
765             goto decline;
766         }
767         pdpy = FindProtoDisplay (from, fromlen, displayNumber);
768         if (!pdpy) {
769
770             /* Check this Display against the Manager's policy */
771             reason = Accept (from, fromlen, displayNumber);
772             if (reason)
773                 goto decline;
774
775             /* Check the Display's stream services against Manager's policy */
776             i = SelectConnectionTypeIndex (&connectionTypes,
777                                            &connectionAddresses);
778             if (i < 0) {
779                 reason = &noValidAddr;
780                 goto decline;
781             }
782         
783             /* The Manager considers this a new session */
784             connectionAddress = &connectionAddresses.data[i];
785             pdpy = NewProtoDisplay (from, fromlen, displayNumber,
786                                     connectionTypes.data[i], connectionAddress,
787                                     NextSessionID());
788             Debug ("NewProtoDisplay 0x%x\n", pdpy);
789             if (!pdpy) {
790                 reason = &outOfMemory;
791                 goto decline;
792             }
793         }
794         if (authorizationNames.length == 0)
795             j = 0;
796         else
797             j = SelectAuthorizationTypeIndex (&authenticationName,
798                                               &authorizationNames);
799         if (j < 0)
800         {
801             reason = &noValidAuth;
802             goto decline;
803         }
804         if (!CheckAuthentication (pdpy,
805                                   &manufacturerDisplayID,
806                                   &authenticationName,
807                                   &authenticationData))
808         {
809             reason = &noAuthentic;
810             goto decline;
811         }
812         if (j < (int)authorizationNames.length)
813         {
814             Xauth   *auth;
815             SetProtoDisplayAuthorization (pdpy,
816                 (unsigned short) authorizationNames.data[j].length,
817                 (char *) authorizationNames.data[j].data);
818             auth = pdpy->xdmcpAuthorization;
819             if (!auth)
820                 auth = pdpy->fileAuthorization;
821             if (auth)
822             {
823                 authorizationName.length = auth->name_length;
824                 authorizationName.data = (CARD8Ptr) auth->name;
825                 authorizationData.length = auth->data_length;
826                 authorizationData.data = (CARD8Ptr) auth->data;
827             }
828         }
829         if (pdpy)
830         {
831             send_accept (from, fromlen, pdpy->sessionID,
832                                         &authenticationName,
833                                         &authenticationData,
834                                         &authorizationName,
835                                         &authorizationData);
836         }
837         else
838         {
839 decline:    ;
840             send_decline (from, fromlen, &authenticationName,
841                                  &authenticationData,
842                                  reason);
843             if (pdpy)
844                 DisposeProtoDisplay (pdpy);
845         }
846     }
847 abort:
848     XdmcpDisposeARRAY16 (&connectionTypes);
849     XdmcpDisposeARRAYofARRAY8 (&connectionAddresses);
850     XdmcpDisposeARRAY8 (&authenticationName);
851     XdmcpDisposeARRAY8 (&authenticationData);
852     XdmcpDisposeARRAYofARRAY8 (&authorizationNames);
853     XdmcpDisposeARRAY8 (&manufacturerDisplayID);
854 }
855
856 send_accept (to, tolen, sessionID,
857              authenticationName, authenticationData,
858              authorizationName, authorizationData)
859     struct sockaddr *to;
860     int             tolen;
861     CARD32          sessionID;
862     ARRAY8Ptr       authenticationName, authenticationData;
863     ARRAY8Ptr       authorizationName, authorizationData;
864 {
865     XdmcpHeader header;
866
867     Debug ("Accept Session ID %d\n", sessionID);
868     header.version = XDM_PROTOCOL_VERSION;
869     header.opcode = (CARD16) ACCEPT;
870     header.length = 4;                      /* session ID */
871     header.length += 2 + authenticationName->length;
872     header.length += 2 + authenticationData->length;
873     header.length += 2 + authorizationName->length;
874     header.length += 2 + authorizationData->length;
875     XdmcpWriteHeader (&buffer, &header);
876     XdmcpWriteCARD32 (&buffer, sessionID);
877     XdmcpWriteARRAY8 (&buffer, authenticationName);
878     XdmcpWriteARRAY8 (&buffer, authenticationData);
879     XdmcpWriteARRAY8 (&buffer, authorizationName);
880     XdmcpWriteARRAY8 (&buffer, authorizationData);
881     XdmcpFlush (xdmcpFd, &buffer, to, tolen);
882 }
883    
884 send_decline (to, tolen, authenticationName, authenticationData, status)
885     struct sockaddr *to;
886     int             tolen;
887     ARRAY8Ptr       authenticationName, authenticationData;
888     ARRAY8Ptr       status;
889 {
890     XdmcpHeader header;
891
892     Debug ("Decline %*.*s\n", status->length, status->length, pS(status->data));
893     header.version = XDM_PROTOCOL_VERSION;
894     header.opcode = (CARD16) DECLINE;
895     header.length = 0;
896     header.length += 2 + status->length;
897     header.length += 2 + authenticationName->length;
898     header.length += 2 + authenticationData->length;
899     XdmcpWriteHeader (&buffer, &header);
900     XdmcpWriteARRAY8 (&buffer, status);
901     XdmcpWriteARRAY8 (&buffer, authenticationName);
902     XdmcpWriteARRAY8 (&buffer, authenticationData);
903     XdmcpFlush (xdmcpFd, &buffer, to, tolen);
904 }
905
906 manage (from, fromlen, length)
907     struct sockaddr *from;
908     int             fromlen;
909     int             length;
910 {
911     CARD32              sessionID;
912     CARD16              displayNumber;
913     ARRAY8              displayClass;
914     int                 expectlen;
915     struct protoDisplay *pdpy;
916     struct display      *d;
917     char                *name = NULL;
918     char                *class = NULL;
919     XdmcpNetaddr        from_save;
920     ARRAY8              clientAddress, clientPort;
921     CARD16              connectionType;
922
923     Debug ("Manage %d\n", length);
924     displayClass.data = 0;
925     displayClass.length = 0;
926     if (XdmcpReadCARD32 (&buffer, &sessionID) &&
927         XdmcpReadCARD16 (&buffer, &displayNumber) &&
928         XdmcpReadARRAY8 (&buffer, &displayClass))
929     {
930         expectlen = 4 +                         /* session ID */
931                     2 +                         /* displayNumber */
932                     2 + displayClass.length;    /* displayClass */
933         if (expectlen != length)
934         {
935             Debug ("Manage length error got %d expect %d\n", length, expectlen);
936             goto abort;
937         }
938         pdpy = FindProtoDisplay (from, fromlen, displayNumber);
939         Debug ("Manage Session ID %d, pdpy 0x%x\n", sessionID, pdpy);
940         if (!pdpy || pdpy->sessionID != sessionID)
941         {
942             /*
943              * We may have already started a session for this display
944              * but it hasn't seen the response in the form of an
945              * XOpenDisplay() yet. So check if it is in the list of active
946              * displays, and if so check that the session id's match.
947              * If all this is true, then we have a duplicate request that
948              * can be ignored.
949              */
950             if (!pdpy 
951                 && (d = FindDisplayByAddress(from, fromlen, displayNumber))
952                 && d->sessionID == sessionID) {
953                      Debug("manage: got duplicate pkt, ignoring\n");
954                      goto abort;
955             }
956             Debug ("Session ID %d refused\n", sessionID);
957             if (pdpy)
958                 Debug ("Existing Session ID %d\n", pdpy->sessionID);
959             send_refuse (from, fromlen, sessionID);
960         }
961         else
962         {
963             name = NetworkAddressToName (pdpy->connectionType,
964                                          &pdpy->connectionAddress,
965                                          pdpy->displayNumber);
966             Debug ("Computed display name: %s\n", name);
967             if (!name)
968             {
969                 send_failed (from, fromlen, "(no name)", sessionID, "out of memory");
970                 goto abort;
971             }
972             d = FindDisplayByName (name);
973             if (d)
974             {
975                 extern void StopDisplay ();
976
977                 Debug ("Terminating active session for %s\n", d->name);
978                 StopDisplay (d);
979             }
980             class = malloc (displayClass.length + 1);
981             if (!class)
982             {
983                 send_failed (from, fromlen, name, sessionID, "out of memory");
984                 goto abort;
985             }
986             if (displayClass.length)
987             {
988                 memmove( class, displayClass.data, displayClass.length);
989                 class[displayClass.length] = '\0';
990             }
991             else
992             {
993                 free ((char *) class);
994                 class = (char *) NULL;
995             }
996             from_save = (XdmcpNetaddr) malloc (fromlen);
997             if (!from_save)
998             {
999                 send_failed (from, fromlen, name, sessionID, "out of memory");
1000                 goto abort;
1001             }
1002             memmove( from_save, from, fromlen);
1003             d = NewDisplay (name, class);
1004             if (!d)
1005             {
1006                 free ((char *) from_save);
1007                 send_failed (from, fromlen, name, sessionID, "out of memory");
1008                 goto abort;
1009             }
1010             d->displayType.location = Foreign;
1011             d->displayType.lifetime = Transient;
1012             d->displayType.origin = FromXDMCP;
1013             d->sessionID = pdpy->sessionID;
1014             d->from = (struct sockaddr *)from_save;
1015             d->fromlen = fromlen;
1016             d->displayNumber = pdpy->displayNumber;
1017 #ifdef BYPASSLOGIN
1018             d->bypassLogin = 0;
1019 #endif /* BYPASSLOGIN */
1020             ClientAddress (from, &clientAddress, &clientPort, &connectionType);
1021             d->useChooser = 0;
1022             if (IsIndirectClient (&clientAddress, connectionType))
1023             {
1024                 Debug ("IsIndirectClient\n");
1025                 ForgetIndirectClient (&clientAddress, connectionType);
1026                 if (UseChooser (&clientAddress, connectionType))
1027                 {
1028                     d->useChooser = 1;
1029                     Debug ("Use chooser for %s\n", d->name);
1030                 }
1031             }
1032             d->clientAddr = clientAddress;
1033             d->connectionType = connectionType;
1034             XdmcpDisposeARRAY8 (&clientPort);
1035             if (pdpy->fileAuthorization)
1036             {
1037                 d->authorizations = (Xauth **) malloc (sizeof (Xauth *));
1038                 if (!d->authorizations)
1039                 {
1040                     free ((char *) from_save);
1041                     free ((char *) d);
1042                     send_failed (from, fromlen, name, sessionID, "out of memory");
1043                     goto abort;
1044                 }
1045                 d->authorizations[0] = pdpy->fileAuthorization;
1046                 d->authNum = 1;
1047                 pdpy->fileAuthorization = 0;
1048             }
1049             DisposeProtoDisplay (pdpy);
1050             Debug ("Starting display %s,%s\n", d->name, d->class);
1051             StartDisplay (d);
1052         }
1053     }
1054 abort:
1055     XdmcpDisposeARRAY8 (&displayClass);
1056     if (name) free ((char*) name);
1057     if (class) free ((char*) class);
1058 }
1059
1060 void SendFailed (d, reason)
1061     struct display  *d;
1062     char            *reason;
1063 {
1064     Debug ("Display start failed, sending Failed\n");
1065     send_failed (d->from, d->fromlen, d->name, d->sessionID, reason);
1066 }
1067
1068 send_failed (from, fromlen, name, sessionID, reason)
1069     struct sockaddr *from;
1070     int             fromlen;
1071     char            *name;
1072     CARD32          sessionID;
1073     char            *reason;
1074 {
1075     static char buf[256];
1076     XdmcpHeader header;
1077     ARRAY8      status;
1078
1079     sprintf (buf, "Session %d failed for display %s: %s",
1080              sessionID, name, reason);
1081     Debug ("Send failed %d %s\n", sessionID, buf);
1082     status.length = strlen (buf);
1083     status.data = (CARD8Ptr) buf;
1084     header.version = XDM_PROTOCOL_VERSION;
1085     header.opcode = (CARD16) FAILED;
1086     header.length = 6 + status.length;
1087     XdmcpWriteHeader (&buffer, &header);
1088     XdmcpWriteCARD32 (&buffer, sessionID);
1089     XdmcpWriteARRAY8 (&buffer, &status);
1090     XdmcpFlush (xdmcpFd, &buffer, from, fromlen);
1091 }
1092
1093 send_refuse (from, fromlen, sessionID)
1094     struct sockaddr *from;
1095     int             fromlen;
1096     CARD32          sessionID;
1097 {
1098     XdmcpHeader header;
1099
1100     Debug ("Send refuse %d\n", sessionID);
1101     header.version = XDM_PROTOCOL_VERSION;
1102     header.opcode = (CARD16) REFUSE;
1103     header.length = 4;
1104     XdmcpWriteHeader (&buffer, &header);
1105     XdmcpWriteCARD32 (&buffer, sessionID);
1106     XdmcpFlush (xdmcpFd, &buffer, from, fromlen);
1107 }
1108
1109 send_alive (from, fromlen, length)
1110     struct sockaddr *from;
1111     int             fromlen;
1112     int             length;
1113 {
1114     CARD32              sessionID;
1115     CARD16              displayNumber;
1116     struct display      *d;
1117     XdmcpHeader         header;
1118     CARD8               sendRunning;
1119     CARD32              sendSessionID;
1120
1121     Debug ("Send alive\n");
1122     if (XdmcpReadCARD16 (&buffer, &displayNumber) &&
1123         XdmcpReadCARD32 (&buffer, &sessionID))
1124     {
1125         if (length == 6)
1126         {
1127             d = FindDisplayBySessionID (sessionID);
1128             if (!d) {
1129                 d = FindDisplayByAddress (from, fromlen, displayNumber);
1130             }
1131             sendRunning = 0;
1132             sendSessionID = 0;
1133             if (d && d->status == running)
1134             {
1135                 if (d->sessionID == sessionID)
1136                     sendRunning = 1;
1137                 sendSessionID = d->sessionID;
1138             }
1139             header.version = XDM_PROTOCOL_VERSION;
1140             header.opcode = (CARD16) ALIVE;
1141             header.length = 5;
1142             Debug ("alive: %d %d\n", sendRunning, sendSessionID);
1143             XdmcpWriteHeader (&buffer, &header);
1144             XdmcpWriteCARD8 (&buffer, sendRunning);
1145             XdmcpWriteCARD32 (&buffer, sendSessionID);
1146             XdmcpFlush (xdmcpFd, &buffer, from, fromlen);
1147         }
1148     }
1149 }
1150
1151 char *
1152 NetworkAddressToHostname (connectionType, connectionAddress)
1153 #if NeedWidePrototypes
1154     int connectionType;
1155 #else
1156     CARD16 connectionType;
1157 #endif
1158     ARRAY8Ptr   connectionAddress;
1159 {
1160     char    *name = 0;
1161
1162     switch (connectionType)
1163     {
1164     case FamilyInternet:
1165         {
1166             struct hostent      *hostent;
1167             char dotted[20];
1168             char *local_name;
1169
1170             hostent = gethostbyaddr ((char *)connectionAddress->data,
1171                                      connectionAddress->length, AF_INET);
1172
1173             if (hostent)
1174                 local_name = hostent->h_name;
1175             else {
1176                 /* can't get name, so use emergency fallback */
1177                 sprintf(dotted, "%d.%d.%d.%d",
1178                         connectionAddress->data[0],
1179                         connectionAddress->data[1],
1180                         connectionAddress->data[2],
1181                         connectionAddress->data[3]);
1182                 local_name = dotted;
1183
1184                 LogError ((unsigned char *)"Cannot convert Internet address %s to host name\n",
1185                           dotted);
1186             }
1187             if (!getString (name, strlen (local_name)))
1188                 break;
1189             strcpy (name, local_name);
1190             break;
1191         }
1192 #ifdef DNET
1193     case FamilyDECnet:
1194         break;
1195 #endif /* DNET */
1196     default:
1197         break;
1198     }
1199     return name;
1200 }
1201
1202 static
1203 HostnameToNetworkAddress (name, connectionType, connectionAddress)
1204 char        *name;
1205 CARD16      connectionType;
1206 ARRAY8Ptr   connectionAddress;
1207 {
1208     switch (connectionType)
1209     {
1210     case FamilyInternet:
1211         {
1212             struct hostent      *hostent;
1213
1214             hostent = gethostbyname (name);
1215             if (!hostent)
1216                 return FALSE;
1217             if (!XdmcpAllocARRAY8 (connectionAddress, hostent->h_length))
1218                 return FALSE;
1219             memmove( connectionAddress->data, hostent->h_addr, hostent->h_length);
1220             return TRUE;
1221         }
1222 #ifdef DNET
1223     case FamilyDECnet:
1224         return FALSE;
1225 #endif
1226     }
1227     return FALSE;
1228 }
1229
1230 /*
1231  * converts a display name into a network address, using
1232  * the same rules as XOpenDisplay (algorithm cribbed from there)
1233  */
1234
1235 static
1236 NameToNetworkAddress(name, connectionTypep, connectionAddress, displayNumber)
1237 char        *name;
1238 CARD16Ptr   connectionTypep;
1239 ARRAY8Ptr   connectionAddress;
1240 CARD16Ptr   displayNumber;
1241 {
1242     char    *colon, *display_number;
1243     char    hostname[1024];
1244     int     dnet = FALSE;
1245     CARD16  number;
1246     CARD16  connectionType;
1247
1248     colon = strchr(name, ':');
1249     if (!colon)
1250         return FALSE;
1251     if (colon != name)
1252     {
1253         if (colon - name > sizeof (hostname))
1254             return FALSE;
1255         strncpy (hostname, name, colon - name);
1256         hostname[colon - name] = '\0';
1257     }
1258     else
1259     {
1260         strcpy (hostname, localHostname ());
1261     }
1262     if (colon[1] == ':')
1263     {
1264         dnet = TRUE;
1265         colon++;
1266     }
1267 #ifndef DNETCONN
1268     if (dnet)
1269         return FALSE;
1270 #endif
1271     display_number = colon + 1;
1272     while (*display_number && *display_number != '.')
1273     {
1274         if (!isascii (*display_number) || !isdigit(*display_number))
1275             return FALSE;
1276     }
1277     if (display_number == colon + 1)
1278         return FALSE;
1279     number = atoi (colon + 1);
1280 #ifdef DNETCONN
1281     if (dnet)
1282         connectionType = FamilyDECnet;
1283     else
1284 #endif
1285         connectionType = FamilyInternet;
1286     if (!HostnameToNetworkAddress (hostname, connectionType, connectionAddress))
1287         return FALSE;
1288     *displayNumber = number;
1289     *connectionTypep = connectionType;
1290     return TRUE;
1291 }