1/*
2 * Copyright (c) 1995 - 2001 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "kx.h"
35
36RCSID("$Id$");
37
38char x_socket[MaxPathLen];
39
40uint32_t display_num;
41char display[MaxPathLen];
42int display_size = sizeof(display);
43char xauthfile[MaxPathLen];
44int xauthfile_size = sizeof(xauthfile);
45u_char cookie[16];
46size_t cookie_len = sizeof(cookie);
47
48#ifndef X_UNIX_PATH
49#define X_UNIX_PATH "/tmp/.X11-unix/X"
50#endif
51
52#ifndef X_PIPE_PATH
53#define X_PIPE_PATH "/tmp/.X11-pipe/X"
54#endif
55
56/*
57 * Allocate a unix domain socket in `s' for display `dpy' and with
58 * filename `pattern'
59 *
60 * 0 if all is OK
61 * -1 if bind failed badly
62 * 1 if dpy is already used */
63
64static int
65try_socket (struct x_socket *s, int dpy, const char *pattern)
66{
67    struct sockaddr_un addr;
68    int fd;
69
70    fd = socket (AF_UNIX, SOCK_STREAM, 0);
71    if (fd < 0)
72	err (1, "socket AF_UNIX");
73    memset (&addr, 0, sizeof(addr));
74    addr.sun_family = AF_UNIX;
75    snprintf (addr.sun_path, sizeof(addr.sun_path), pattern, dpy);
76    if(bind(fd,
77	    (struct sockaddr *)&addr,
78	    sizeof(addr)) < 0) {
79	close (fd);
80	if (errno == EADDRINUSE ||
81	    errno == EACCES  /* Cray return EACCESS */
82#ifdef ENOTUNIQ
83	    || errno == ENOTUNIQ /* bug in Solaris 2.4 */
84#endif
85	    )
86	    return 1;
87	else
88	    return -1;
89    }
90    s->fd = fd;
91    s->pathname = strdup (addr.sun_path);
92    if (s->pathname == NULL)
93	errx (1, "strdup: out of memory");
94    s->flags = UNIX_SOCKET;
95    return 0;
96}
97
98#ifdef MAY_HAVE_X11_PIPES
99/*
100 * Allocate a stream (masqueraded as a named pipe)
101 *
102 * 0 if all is OK
103 * -1 if bind failed badly
104 * 1 if dpy is already used
105 */
106
107static int
108try_pipe (struct x_socket *s, int dpy, const char *pattern)
109{
110    char path[MAXPATHLEN];
111    int ret;
112    int fd;
113    int pipefd[2];
114
115    snprintf (path, sizeof(path), pattern, dpy);
116    fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0600);
117    if (fd < 0) {
118	if (errno == EEXIST)
119	    return 1;
120	else
121	    return -1;
122    }
123
124    close (fd);
125
126    ret = pipe (pipefd);
127    if (ret < 0)
128	err (1, "pipe");
129
130    ret = ioctl (pipefd[1], I_PUSH, "connld");
131    if (ret < 0) {
132	if(errno == ENOSYS)
133	    return -1;
134	err (1, "ioctl I_PUSH");
135    }
136
137    ret = fattach (pipefd[1], path);
138    if (ret < 0)
139	err (1, "fattach %s", path);
140
141    s->fd  = pipefd[0];
142    close (pipefd[1]);
143    s->pathname = strdup (path);
144    if (s->pathname == NULL)
145	errx (1, "strdup: out of memory");
146    s->flags = STREAM_PIPE;
147    return 0;
148}
149#endif /* MAY_HAVE_X11_PIPES */
150
151/*
152 * Try to create a TCP socket in `s' corresponding to display `dpy'.
153 *
154 * 0 if all is OK
155 * -1 if bind failed badly
156 * 1 if dpy is already used
157 */
158
159static int
160try_tcp (struct x_socket *s, int dpy)
161{
162    struct sockaddr_in tcpaddr;
163    struct in_addr local;
164    int one = 1;
165    int fd;
166
167    memset(&local, 0, sizeof(local));
168    local.s_addr = htonl(INADDR_LOOPBACK);
169
170    fd = socket (AF_INET, SOCK_STREAM, 0);
171    if (fd < 0)
172	err (1, "socket AF_INET");
173#if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
174    setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&one,
175		sizeof(one));
176#endif
177    memset (&tcpaddr, 0, sizeof(tcpaddr));
178    tcpaddr.sin_family = AF_INET;
179    tcpaddr.sin_addr = local;
180    tcpaddr.sin_port = htons(6000 + dpy);
181    if (bind (fd, (struct sockaddr *)&tcpaddr,
182	      sizeof(tcpaddr)) < 0) {
183	close (fd);
184	if (errno == EADDRINUSE)
185	    return 1;
186	else
187	    return -1;
188    }
189    s->fd = fd;
190    s->pathname = NULL;
191    s->flags = TCP;
192    return 0;
193}
194
195/*
196 * The potential places to create unix sockets.
197 */
198
199static char *x_sockets[] = {
200X_UNIX_PATH "%u",
201"/var/X/.X11-unix/X" "%u",
202"/usr/spool/sockets/X11/" "%u",
203NULL
204};
205
206/*
207 * Dito for stream pipes.
208 */
209
210#ifdef MAY_HAVE_X11_PIPES
211static char *x_pipes[] = {
212X_PIPE_PATH "%u",
213"/var/X/.X11-pipe/X" "%u",
214NULL
215};
216#endif
217
218/*
219 * Create the directory corresponding to dirname of `path' or fail.
220 */
221
222static void
223try_mkdir (const char *path)
224{
225    char *dir;
226    char *p;
227    int oldmask;
228
229    if((dir = strdup (path)) == NULL)
230	errx (1, "strdup: out of memory");
231    p = strrchr (dir, '/');
232    if (p)
233	*p = '\0';
234
235    oldmask = umask(0);
236    mkdir (dir, 01777);
237    umask (oldmask);
238    free (dir);
239}
240
241/*
242 * Allocate a display, returning the number of sockets in `number' and
243 * all the corresponding sockets in `sockets'.  If `tcp_socket' is
244 * true, also allcoaet a TCP socket.
245 *
246 * The return value is the display allocated or -1 if an error occurred.
247 */
248
249int
250get_xsockets (int *number, struct x_socket **sockets, int tcp_socket)
251{
252     int dpy;
253     struct x_socket *s;
254     int n;
255     int i;
256
257     s = malloc (sizeof(*s) * 5);
258     if (s == NULL)
259	 errx (1, "malloc: out of memory");
260
261     try_mkdir (X_UNIX_PATH);
262     try_mkdir (X_PIPE_PATH);
263
264     for(dpy = 4; dpy < 256; ++dpy) {
265	 char **path;
266	 int tmp = 0;
267
268	 n = 0;
269	 for (path = x_sockets; *path; ++path) {
270	     tmp = try_socket (&s[n], dpy, *path);
271	     if (tmp == -1) {
272		 if (errno != ENOTDIR && errno != ENOENT)
273		     err(1, "failed to open '%s'", *path);
274	     } else if (tmp == 1) {
275		 while(--n >= 0) {
276		     close (s[n].fd);
277		     free (s[n].pathname);
278		 }
279		 break;
280	     } else if (tmp == 0)
281		 ++n;
282	 }
283	 if (tmp == 1)
284	     continue;
285
286#ifdef MAY_HAVE_X11_PIPES
287	 for (path = x_pipes; *path; ++path) {
288	     tmp = try_pipe (&s[n], dpy, *path);
289	     if (tmp == -1) {
290		 if (errno != ENOTDIR && errno != ENOENT && errno != ENOSYS)
291		     err(1, "failed to open '%s'", *path);
292	     } else if (tmp == 1) {
293		 while (--n >= 0) {
294		     close (s[n].fd);
295		     free (s[n].pathname);
296		 }
297		 break;
298	     } else if (tmp == 0)
299		 ++n;
300	 }
301
302	 if (tmp == 1)
303	     continue;
304#endif
305
306	 if (tcp_socket) {
307	     tmp = try_tcp (&s[n], dpy);
308	     if (tmp == -1)
309		 err(1, "failed to open tcp stocket");
310	     else if (tmp == 1) {
311		 while (--n >= 0) {
312		     close (s[n].fd);
313		     free (s[n].pathname);
314		 }
315		 break;
316	     } else if (tmp == 0)
317		 ++n;
318	 }
319	 break;
320     }
321     if (dpy == 256)
322	 errx (1, "no free x-servers");
323     for (i = 0; i < n; ++i)
324	 if (s[i].flags & LISTENP
325	     && listen (s[i].fd, SOMAXCONN) < 0)
326	     err (1, "listen %s", s[i].pathname ? s[i].pathname : "tcp");
327     *number = n;
328     *sockets = s;
329     return dpy;
330}
331
332/*
333 * Change owner on the `n' sockets in `sockets' to `uid', `gid'.
334 * Return 0 is succesful or -1 if an error occurred.
335 */
336
337int
338chown_xsockets (int n, struct x_socket *sockets, uid_t uid, gid_t gid)
339{
340    int i;
341
342    for (i = 0; i < n; ++i)
343	if (sockets[i].pathname != NULL)
344	    if (chown (sockets[i].pathname, uid, gid) < 0)
345		return -1;
346    return 0;
347}
348
349/*
350 * Connect to local display `dnr' with local transport or TCP.
351 * Return a file descriptor.
352 */
353
354int
355connect_local_xsocket (unsigned dnr)
356{
357     int fd;
358     char **path;
359
360     for (path = x_sockets; *path; ++path) {
361	 struct sockaddr_un addr;
362
363	 fd = socket (AF_UNIX, SOCK_STREAM, 0);
364	 if (fd < 0)
365	     break;
366	 memset (&addr, 0, sizeof(addr));
367	 addr.sun_family = AF_UNIX;
368	 snprintf (addr.sun_path, sizeof(addr.sun_path), *path, dnr);
369	 if (connect (fd, (struct sockaddr *)&addr, sizeof(addr)) == 0)
370	     return fd;
371	 close(fd);
372     }
373     {
374	 struct sockaddr_in addr;
375
376	 fd = socket(AF_INET, SOCK_STREAM, 0);
377	 if (fd < 0)
378	     err (1, "socket AF_INET");
379	 memset (&addr, 0, sizeof(addr));
380	 addr.sin_family = AF_INET;
381	 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
382	 addr.sin_port = htons(6000 + dnr);
383	 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0)
384	     return fd;
385	 close(fd);
386     }
387     err (1, "connecting to local display %u", dnr);
388}
389
390/*
391 * Create a cookie file with a random cookie for the localhost.  The
392 * file name will be stored in `xauthfile' (but not larger than
393 * `xauthfile_size'), and the cookie returned in `cookie', `cookie_sz'.
394 * Return 0 if succesful, or errno.
395 */
396
397int
398create_and_write_cookie (char *file,
399			 size_t file_size,
400			 u_char *cookie_buf,
401			 size_t cookie_sz)
402{
403     Xauth auth;
404     char tmp[64];
405     int fd;
406     FILE *f;
407     char hostname[MaxHostNameLen];
408     int saved_errno;
409
410     gethostname (hostname, sizeof(hostname));
411
412     auth.family = FamilyLocal;
413     auth.address = hostname;
414     auth.address_length = strlen(auth.address);
415     snprintf (tmp, sizeof(tmp), "%d", display_num);
416     auth.number_length = strlen(tmp);
417     auth.number = tmp;
418     auth.name = COOKIE_TYPE;
419     auth.name_length = strlen(auth.name);
420     auth.data_length = cookie_sz;
421     auth.data = (char*)cookie_buf;
422#ifdef KRB5
423     krb5_generate_random_block (cookie_buf, cookie_sz);
424#else
425     krb_generate_random_block (cookie_buf, cookie_sz);
426#endif
427
428     strlcpy(file, "/tmp/AXXXXXX", file_size);
429     fd = mkstemp(file);
430     if(fd < 0) {
431	 saved_errno = errno;
432	 syslog(LOG_ERR, "create_and_write_cookie: mkstemp: %m");
433         return saved_errno;
434     }
435     f = fdopen(fd, "r+");
436     if(f == NULL){
437	 saved_errno = errno;
438	 close(fd);
439	 return errno;
440     }
441     if(XauWriteAuth(f, &auth) == 0) {
442	 saved_errno = errno;
443	 fclose(f);
444	 return saved_errno;
445     }
446
447     /*
448      * I would like to write a cookie for localhost:n here, but some
449      * stupid code in libX11 will not look for cookies of that type,
450      * so we are forced to use FamilyWild instead.
451      */
452
453     auth.family  = FamilyWild;
454     auth.address_length = 0;
455
456     if (XauWriteAuth(f, &auth) == 0) {
457	 saved_errno = errno;
458	 fclose (f);
459	 return saved_errno;
460     }
461
462     if(fclose(f))
463	 return errno;
464     return 0;
465}
466
467/*
468 * Verify and remove cookies.  Read and parse a X-connection from
469 * `fd'. Check the cookie used is the same as in `cookie'.  Remove the
470 * cookie and copy the rest of it to `sock'.
471 * Expect cookies iff cookiesp.
472 * Return 0 iff ok.
473 *
474 * The protocol is as follows:
475 *
476 * C->S:	[Bl]				1
477 *		unused				1
478 *		protocol major version		2
479 *		protocol minor version		2
480 *		length of auth protocol name(n)	2
481 *		length of auth protocol data	2
482 *		unused				2
483 *		authorization protocol name	n
484 *		pad				pad(n)
485 *		authorization protocol data	d
486 *		pad				pad(d)
487 *
488 * S->C:	Failed
489 *		0				1
490 *		length of reason		1
491 *		protocol major version		2
492 *		protocol minor version		2
493 *		length in 4 bytes unit of
494 *		additional data (n+p)/4		2
495 *		reason				n
496 *		unused				p = pad(n)
497 */
498
499int
500verify_and_remove_cookies (int fd, int sock, int cookiesp)
501{
502     u_char beg[12];
503     int bigendianp;
504     unsigned n, d, npad, dpad;
505     char *protocol_name, *protocol_data;
506     u_char zeros[6] = {0, 0, 0, 0, 0, 0};
507     u_char refused[20] = {0, 10,
508			   0, 0, /* protocol major version  */
509			   0, 0, /* protocol minor version */
510			   0, 0, /* length of additional data / 4 */
511			   'b', 'a', 'd', ' ', 'c', 'o', 'o', 'k', 'i', 'e',
512			   0, 0};
513
514     if (net_read (fd, beg, sizeof(beg)) != sizeof(beg))
515	  return 1;
516     if (net_write (sock, beg, 6) != 6)
517	  return 1;
518     bigendianp = beg[0] == 'B';
519     if (bigendianp) {
520	  n = (beg[6] << 8) | beg[7];
521	  d = (beg[8] << 8) | beg[9];
522     } else {
523	  n = (beg[7] << 8) | beg[6];
524	  d = (beg[9] << 8) | beg[8];
525     }
526     npad = (4 - (n % 4)) % 4;
527     dpad = (4 - (d % 4)) % 4;
528     protocol_name = malloc(n + npad);
529     if (n + npad != 0 && protocol_name == NULL)
530	 return 1;
531     protocol_data = malloc(d + dpad);
532     if (d + dpad != 0 && protocol_data == NULL) {
533	 free (protocol_name);
534	 return 1;
535     }
536     if (net_read (fd, protocol_name, n + npad) != n + npad)
537	 goto fail;
538     if (net_read (fd, protocol_data, d + dpad) != d + dpad)
539	 goto fail;
540     if (cookiesp) {
541	 if (strncmp (protocol_name, COOKIE_TYPE, strlen(COOKIE_TYPE)) != 0)
542	     goto refused;
543	 if (d != cookie_len ||
544	     memcmp (protocol_data, cookie, cookie_len) != 0)
545	     goto refused;
546     }
547     free (protocol_name);
548     free (protocol_data);
549     if (net_write (sock, zeros, 6) != 6)
550	  return 1;
551     return 0;
552refused:
553     refused[2] = beg[2];
554     refused[3] = beg[3];
555     refused[4] = beg[4];
556     refused[5] = beg[5];
557     if (bigendianp)
558	 refused[7] = 3;
559     else
560	 refused[6] = 3;
561
562     net_write (fd, refused, sizeof(refused));
563fail:
564     free (protocol_name);
565     free (protocol_data);
566     return 1;
567}
568
569/*
570 * Return 0 iff `cookie' is compatible with the cookie for the
571 * localhost with name given in `ai' (or `hostname') and display
572 * number in `disp_nr'.
573 */
574
575static int
576match_local_auth (Xauth* auth,
577		  struct addrinfo *ai, const char *hostname, int disp_nr)
578{
579    int auth_disp;
580    char *tmp_disp;
581    struct addrinfo *a;
582
583    tmp_disp = malloc(auth->number_length + 1);
584    if (tmp_disp == NULL)
585	return -1;
586    memcpy(tmp_disp, auth->number, auth->number_length);
587    tmp_disp[auth->number_length] = '\0';
588    auth_disp = atoi(tmp_disp);
589    free (tmp_disp);
590    if (auth_disp != disp_nr)
591	return 1;
592    for (a = ai; a != NULL; a = a->ai_next) {
593	if ((auth->family == FamilyLocal
594	     || auth->family == FamilyWild)
595	    && a->ai_canonname != NULL
596	    && strncmp (auth->address,
597			a->ai_canonname,
598			auth->address_length) == 0)
599	    return 0;
600    }
601    if (hostname != NULL
602	&& (auth->family    == FamilyLocal
603	    || auth->family == FamilyWild)
604	&& strncmp (auth->address, hostname, auth->address_length) == 0)
605	return 0;
606    return 1;
607}
608
609/*
610 * Find `our' cookie from the cookie file `f' and return it or NULL.
611 */
612
613static Xauth*
614find_auth_cookie (FILE *f)
615{
616    Xauth *ret = NULL;
617    char local_hostname[MaxHostNameLen];
618    char *display_str = getenv("DISPLAY");
619    char d[MaxHostNameLen + 4];
620    char *colon;
621    struct addrinfo *ai;
622    struct addrinfo hints;
623    int disp;
624    int error;
625
626    if(display_str == NULL)
627	display_str = ":0";
628    strlcpy(d, display_str, sizeof(d));
629    display_str = d;
630    colon = strchr (display_str, ':');
631    if (colon == NULL)
632	disp = 0;
633    else {
634	*colon = '\0';
635	disp = atoi (colon + 1);
636    }
637    if (strcmp (display_str, "") == 0
638	|| strncmp (display_str, "unix", 4) == 0
639	|| strncmp (display_str, "localhost", 9) == 0) {
640	gethostname (local_hostname, sizeof(local_hostname));
641	display_str = local_hostname;
642    }
643    memset (&hints, 0, sizeof(hints));
644    hints.ai_flags    = AI_CANONNAME;
645    hints.ai_socktype = SOCK_STREAM;
646    hints.ai_protocol = IPPROTO_TCP;
647
648    error = getaddrinfo (display_str, NULL, &hints, &ai);
649    if (error)
650	ai = NULL;
651
652    for (; (ret = XauReadAuth (f)) != NULL; XauDisposeAuth(ret)) {
653	if (match_local_auth (ret, ai, display_str, disp) == 0) {
654	    if (ai != NULL)
655		freeaddrinfo (ai);
656	    return ret;
657	}
658    }
659    if (ai != NULL)
660	freeaddrinfo (ai);
661    return NULL;
662}
663
664/*
665 * Get rid of the cookie that we were sent and get the correct one
666 * from our own cookie file instead.
667 */
668
669int
670replace_cookie(int xserver, int fd, char *filename, int cookiesp) /* XXX */
671{
672     u_char beg[12];
673     int bigendianp;
674     unsigned n, d, npad, dpad;
675     FILE *f;
676     u_char zeros[6] = {0, 0, 0, 0, 0, 0};
677
678     if (net_read (fd, beg, sizeof(beg)) != sizeof(beg))
679	  return 1;
680     if (net_write (xserver, beg, 6) != 6)
681	  return 1;
682     bigendianp = beg[0] == 'B';
683     if (bigendianp) {
684	  n = (beg[6] << 8) | beg[7];
685	  d = (beg[8] << 8) | beg[9];
686     } else {
687	  n = (beg[7] << 8) | beg[6];
688	  d = (beg[9] << 8) | beg[8];
689     }
690     if (n != 0 || d != 0)
691	  return 1;
692     f = fopen(filename, "r");
693     if (f != NULL) {
694	 Xauth *auth = find_auth_cookie (f);
695	 u_char len[6] = {0, 0, 0, 0, 0, 0};
696
697	 fclose (f);
698
699	 if (auth != NULL) {
700	     n = auth->name_length;
701	     d = auth->data_length;
702	 } else {
703	     n = 0;
704	     d = 0;
705	 }
706	 if (bigendianp) {
707	     len[0] = n >> 8;
708	     len[1] = n & 0xFF;
709	     len[2] = d >> 8;
710	     len[3] = d & 0xFF;
711	 } else {
712	     len[0] = n & 0xFF;
713	     len[1] = n >> 8;
714	     len[2] = d & 0xFF;
715	     len[3] = d >> 8;
716	 }
717	 if (net_write (xserver, len, 6) != 6) {
718	     XauDisposeAuth(auth);
719	     return 1;
720	 }
721	 if(n != 0 && net_write (xserver, auth->name, n) != n) {
722	     XauDisposeAuth(auth);
723	     return 1;
724	 }
725	 npad = (4 - (n % 4)) % 4;
726	 if (npad && net_write (xserver, zeros, npad) != npad) {
727	     XauDisposeAuth(auth);
728	     return 1;
729	 }
730	 if (d != 0 && net_write (xserver, auth->data, d) != d) {
731	     XauDisposeAuth(auth);
732	     return 1;
733	 }
734	 XauDisposeAuth(auth);
735	 dpad = (4 - (d % 4)) % 4;
736	 if (dpad && net_write (xserver, zeros, dpad) != dpad)
737	     return 1;
738     } else {
739	 if(net_write(xserver, zeros, 6) != 6)
740	     return 1;
741     }
742     return 0;
743}
744
745/*
746 * Some simple controls on the address and corresponding socket
747 */
748
749int
750suspicious_address (int sock, struct sockaddr *addr)
751{
752    char data[40];
753    socklen_t len = sizeof(data);
754
755    switch (addr->sa_family) {
756    case AF_INET:
757	return ((struct sockaddr_in *)addr)->sin_addr.s_addr !=
758	    htonl(INADDR_LOOPBACK)
759#if defined(IP_OPTIONS) && defined(HAVE_GETSOCKOPT)
760	    || getsockopt (sock, IPPROTO_IP, IP_OPTIONS, data, &len) < 0
761	    || len != 0
762#endif
763	    ;
764	break;
765#ifdef HAVE_IPV6
766    case AF_INET6:
767	/* XXX check route headers */
768	return !IN6_IS_ADDR_LOOPBACK(&((struct sockaddr_in6*)addr)->sin6_addr);
769#endif
770    default:
771	return 1;
772    }
773}
774
775/*
776 * This really sucks, but these functions are used and if we're not
777 * linking against libkrb they don't exist.  Using the heimdal storage
778 * functions will not work either cause we do not always link with
779 * libkrb5 either.
780 */
781
782int
783kx_get_int(void *f, uint32_t *to, int size, int lsb)
784{
785    int i;
786    unsigned char *from = (unsigned char *)f;
787
788    *to = 0;
789    if(lsb){
790	for(i = size-1; i >= 0; i--)
791	    *to = (*to << 8) | from[i];
792    }else{
793	for(i = 0; i < size; i++)
794	    *to = (*to << 8) | from[i];
795    }
796    return size;
797}
798
799int
800kx_put_int(uint32_t from, void *to, size_t rem, int size)
801{
802    int i;
803    unsigned char *p = (unsigned char *)to;
804
805    if (rem < size)
806	return -1;
807
808    for(i = size - 1; i >= 0; i--){
809	p[i] = from & 0xff;
810	from >>= 8;
811    }
812    return size;
813}
814