1/*
2 *
3Copyright 1989, 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:  Jim Fulton, MIT X Consortium
26 */
27
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
32#ifdef WIN32
33#include <X11/Xwinsock.h>
34#define EPROTOTYPE WSAEPROTOTYPE
35#endif
36#include <X11/X.h>
37#include <signal.h>
38#include <setjmp.h>
39#include <ctype.h>
40#ifndef __TYPES__
41#include <sys/types.h>
42#define __TYPES__
43#endif
44#ifndef WIN32
45#include <sys/socket.h>
46#include <netdb.h>
47#include <netinet/in.h>
48#include <arpa/inet.h>
49#ifdef HAVE_NET_ERRNO_H
50#include <net/errno.h>
51#endif /* HAVE_NET_ERRNO_H */
52#endif /* !WIN32 */
53#include <errno.h>
54#include "xauth.h"
55
56#include <sys/stat.h>
57#include <limits.h>
58
59#ifndef WIN32
60#include <arpa/inet.h>
61#endif
62
63#if defined(IPv6) && !defined(AF_INET6)
64#error "Cannot build IPv6 support without AF_INET6"
65#endif
66
67const char *
68get_hostname (Xauth *auth)
69{
70    if (auth->address_length == 0)
71	return "Illegal Address";
72#ifdef TCPCONN
73    if (auth->family == FamilyInternet
74#ifdef IPv6
75      || auth->family == FamilyInternet6
76#endif
77	)
78    {
79	static struct hostent *hp = NULL;
80#ifdef IPv6
81	static char addr[INET6_ADDRSTRLEN+2];
82#elif defined(HAVE_INET_NTOP)
83	static char addr[INET_ADDRSTRLEN];
84#endif
85	int af;
86
87#ifdef IPv6
88	if (auth->family == FamilyInternet6)
89	    af = AF_INET6;
90	else
91#endif
92	    af = AF_INET;
93	if (no_name_lookups == False) {
94	    hp = gethostbyaddr (auth->address, auth->address_length, af);
95	}
96	if (hp)
97	  return (hp->h_name);
98#ifdef IPv6
99	else if (af == AF_INET6) {
100	  /* Add [] for clarity to distinguish between address & display,
101	     like RFC 2732 for URL's.  Not required, since X display syntax
102	     always ends in :<display>, but makes it easier for people to read
103	     and less confusing to those who expect the RFC 2732 style. */
104	  addr[0] = '[';
105	  if (inet_ntop(af, auth->address, addr + 1, INET6_ADDRSTRLEN) == NULL)
106	    return NULL;
107	  strcat(addr, "]");
108          return addr;
109	}
110#endif
111	else {
112#ifdef HAVE_INET_NTOP
113	  return (inet_ntop(af, auth->address, addr, sizeof(addr)));
114#else
115	  return (inet_ntoa(*((struct in_addr *)(auth->address))));
116#endif
117	}
118    }
119#endif
120
121    return (NULL);
122}
123
124#if defined(TCPCONN) && !defined(HAVE_GETADDRINFO)
125/*
126 * cribbed from lib/X/XConnDis.c
127 */
128static Bool
129get_inet_address(char *name, unsigned int *resultp)
130{
131    unsigned int hostinetaddr = inet_addr (name);
132    struct hostent *host_ptr;
133    struct sockaddr_in inaddr;		/* dummy variable for size calcs */
134
135#ifndef INADDR_NONE
136#define INADDR_NONE -1
137#endif
138
139    if (hostinetaddr == INADDR_NONE) {
140	if ((host_ptr = gethostbyname (name)) == NULL) {
141	    /* No such host! */
142	    errno = EINVAL;
143	    return False;
144	}
145	/* Check the address type for an internet host. */
146	if (host_ptr->h_addrtype != AF_INET) {
147	    /* Not an Internet host! */
148	    errno = EPROTOTYPE;
149	    return False;
150	}
151
152	memmove( (char *)&hostinetaddr, (char *)host_ptr->h_addr,
153	      sizeof(inaddr.sin_addr));
154    }
155    *resultp = hostinetaddr;
156    return True;
157}
158#endif
159
160
161struct addrlist *get_address_info (
162    int family,
163    const char *fulldpyname,
164    int prefix,
165    char *host)
166{
167    struct addrlist *retval = NULL;
168    int len = 0;
169    const void *src = NULL;
170#ifdef TCPCONN
171#ifdef HAVE_GETADDRINFO
172    struct addrlist *lastrv = NULL;
173    struct addrinfo *firstai = NULL;
174    struct addrinfo hints;
175#else
176    unsigned int hostinetaddr;
177#endif
178#endif
179    char buf[255];
180
181    /*
182     * based on the family, set the pointer src to the start of the address
183     * information to be copied and set len to the number of bytes.
184     */
185    switch (family) {
186      case FamilyLocal:			/* hostname/unix:0 */
187					/* handle unix:0 and :0 specially */
188	if (prefix == 0 && (strncmp (fulldpyname, "unix:", 5) == 0 ||
189			    fulldpyname[0] == ':')) {
190
191	    if (!get_local_hostname (buf, sizeof buf)) {
192		len = 0;
193	    } else {
194		src = buf;
195		len = strlen (buf);
196	    }
197	} else {
198	    char path[PATH_MAX];
199	    struct stat sbuf;
200	    int is_path_to_socket = 0;
201
202#ifdef HAVE_STRLCPY
203	    strlcpy(path, fulldpyname, sizeof(path));
204#else
205	    strncpy(path, fulldpyname, sizeof(path) - 1);
206	    path[sizeof(path) - 1] = '\0';
207#endif
208	    if (0 == stat(path, &sbuf) && S_ISSOCK(sbuf.st_mode) ) {
209		is_path_to_socket = 1;
210	    } else {
211		char *dot = strrchr(path, '.');
212		if (dot) {
213		    *dot = '\0';
214		    /* screen = atoi(dot + 1); */
215		    if (0 == stat(path, &sbuf) && S_ISSOCK(sbuf.st_mode)) {
216		        is_path_to_socket = 1;
217		    }
218		}
219	    }
220
221	    if (is_path_to_socket) {
222		/* Use the bundle id (part preceding : in the basename) as our src id */
223		char *c;
224		c = strrchr(fulldpyname, '/');
225#ifdef HAVE_STRLCPY
226		strlcpy(buf, (NULL != c) ? c + 1 : fulldpyname, sizeof(buf));
227#else
228		strncpy(buf, (NULL != c) ? c + 1 : fulldpyname, sizeof(buf) - 1);
229		buf[sizeof(buf) - 1] = '\0';
230#endif
231
232		c = strchr(buf, ':');
233
234		/* In the legacy case with no bundle id, use the full path */
235		if(c == buf) {
236			src = fulldpyname;
237		} else {
238			*c = '\0';
239			src = buf;
240		}
241
242		len = strlen(src);
243	    } else {
244		src = fulldpyname;
245		len = prefix;
246            }
247	}
248	break;
249      case FamilyInternet:		/* host:0 */
250#ifdef TCPCONN
251#ifdef HAVE_GETADDRINFO
252#ifdef IPv6
253      case FamilyInternet6:
254#endif
255	memset(&hints, 0, sizeof(hints));
256#ifdef IPv6
257	hints.ai_family = PF_UNSPEC; /* IPv4 or IPv6 */
258#else
259	hints.ai_family = PF_INET;   /* IPv4 only */
260#endif
261	hints.ai_socktype = SOCK_STREAM; /* only interested in TCP */
262	hints.ai_protocol = 0;
263        if (getaddrinfo(host,NULL,&hints,&firstai) !=0) return NULL;
264	for (struct addrinfo *ai = firstai; ai != NULL; ai = ai->ai_next) {
265	    struct addrlist *duplicate;
266
267            len = 0;
268	    if (ai->ai_family == AF_INET) {
269		struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
270		src = &(sin->sin_addr);
271                if (*(const in_addr_t *) src == htonl(INADDR_LOOPBACK)) {
272                    family = FamilyLocal;
273                    if (get_local_hostname (buf, sizeof buf)) {
274                        src = buf;
275                        len = strlen (buf);
276                    } else
277                        src = NULL;
278                } else {
279                    len = sizeof(sin->sin_addr);
280                    family = FamilyInternet;
281                }
282#ifdef IPv6
283	    } else if (ai->ai_family == AF_INET6) {
284		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr;
285		src = &(sin6->sin6_addr);
286                if (!IN6_IS_ADDR_V4MAPPED((const struct in6_addr *)src)) {
287                    if (IN6_IS_ADDR_LOOPBACK((const struct in6_addr *)src)) {
288                        family = FamilyLocal;
289                        if (get_local_hostname (buf, sizeof buf)) {
290                            src = buf;
291                            len = strlen (buf);
292                        } else
293                            src = NULL;
294                    } else {
295                        len = sizeof(sin6->sin6_addr);
296                        family = FamilyInternet6;
297                    }
298                } else {
299                    src = &(sin6->sin6_addr.s6_addr[12]);
300                    len = sizeof(((struct sockaddr_in *)
301                                  ai->ai_addr)->sin_addr);
302                    family = FamilyInternet;
303                }
304#endif
305	    }
306
307	    for(duplicate = retval; duplicate != NULL; duplicate = duplicate->next) {
308		if(duplicate->family == family && duplicate->len == len &&
309                   memcmp(duplicate->address, src, len) == 0) {
310		    break;
311                }
312	    }
313
314	    if (len > 0 && src != NULL && duplicate == NULL) {
315		struct addrlist *newrv = malloc (sizeof(struct addrlist));
316		if (newrv) {
317		    newrv->address = malloc (len);
318		    if (newrv->address) {
319			memcpy(newrv->address, src, len);
320			newrv->next = NULL;
321			newrv->family = family;
322			newrv->len = len;
323			if (retval == NULL) {
324			    lastrv = retval = newrv;
325			} else {
326			    lastrv->next = newrv;
327			    lastrv = newrv;
328			}
329		    } else {
330			free(newrv);
331		    }
332		}
333	    }
334	    /* reset to avoid copying into list twice */
335	    len = 0;
336	    src = NULL;
337	}
338	freeaddrinfo(firstai);
339	break;
340#else /* !HAVE_GETADDRINFO */
341	if (!get_inet_address (host, &hostinetaddr)) return NULL;
342	src = (char *) &hostinetaddr;
343        if (*(const in_addr_t *) src == htonl(INADDR_LOOPBACK)) {
344            family = FamilyLocal;
345            if (get_local_hostname (buf, sizeof buf)) {
346                src = buf;
347                len = strlen (buf);
348            } else {
349                len = 0;
350                src = NULL;
351            }
352        } else
353            len = 4; /* sizeof inaddr.sin_addr, would fail on Cray */
354	break;
355#endif /* HAVE_GETADDRINFO */
356#else /* !TCPCONN */
357	return NULL;
358#endif
359      case FamilyDECnet:		/* host::0 */
360	/* fall through since we don't have code for it */
361      default:
362	src = NULL;
363	len = 0;
364    }
365
366    /*
367     * if source was provided, allocate space and copy it
368     */
369    if (len > 0 && src != NULL) {
370	retval = malloc (sizeof(struct addrlist));
371	if (retval) {
372	    retval->address = malloc (len);
373	    if (retval->address) {
374		memcpy(retval->address, src, len);
375		retval->next = NULL;
376		retval->family = family;
377		retval->len = len;
378	    } else {
379		free(retval);
380		retval = NULL;
381	    }
382	}
383    }
384    return retval;
385}
386