source: trunk/third/evolution/e-util/e-host-utils.c @ 18142

Revision 18142, 11.3 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18141, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2/* e-host-utils.c
3 *
4 * Copyright (C) 2001  Ximian, Inc.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2 of the GNU General Public
8 * License as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Author: Chris Toshok, Jeffrey Stedfast
21 */
22
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <glib.h>
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <sys/types.h>
34#include <sys/socket.h>
35#include <arpa/inet.h>
36#include <errno.h>
37
38#include "e-host-utils.h"
39
40
41#if !defined (HAVE_GETHOSTBYNAME_R) || !defined (HAVE_GETHOSTBYADDR_R)
42G_LOCK_DEFINE_STATIC (gethost_mutex);
43#endif
44
45
46#define GETHOST_PROCESS(h, host, buf, buflen, herr) G_STMT_START {     \
47        int num_aliases = 0, num_addrs = 0;                            \
48        int req_length;                                                \
49        char *p;                                                       \
50        int i;                                                         \
51                                                                       \
52        /* check to make sure we have enough room in our buffer */     \
53        req_length = 0;                                                \
54        if (h->h_aliases) {                                            \
55                for (i = 0; h->h_aliases[i]; i++)                      \
56                        req_length += strlen (h->h_aliases[i]) + 1;    \
57                num_aliases = i;                                       \
58        }                                                              \
59                                                                       \
60        if (h->h_addr_list) {                                          \
61                for (i = 0; h->h_addr_list[i]; i++)                    \
62                        req_length += h->h_length;                     \
63                num_addrs = i;                                         \
64        }                                                              \
65                                                                       \
66        req_length += sizeof (char *) * (num_aliases + 1);             \
67        req_length += sizeof (char *) * (num_addrs + 1);               \
68        req_length += strlen (h->h_name) + 1;                          \
69                                                                       \
70        if (buflen < req_length) {                                     \
71                *herr = ERANGE;                                        \
72                G_UNLOCK (gethost_mutex);                              \
73                return ERANGE;                                         \
74        }                                                              \
75                                                                       \
76        /* we store the alias/addr pointers in the buffer */           \
77        /* their addresses here. */                                    \
78        p = buf;                                                       \
79        if (num_aliases) {                                             \
80                host->h_aliases = (char **) p;                         \
81                p += sizeof (char *) * (num_aliases + 1);              \
82        } else                                                         \
83                host->h_aliases = NULL;                                \
84                                                                       \
85        if (num_addrs) {                                               \
86                host->h_addr_list = (char **) p;                       \
87                p += sizeof (char *) * (num_addrs + 1);                \
88        } else                                                         \
89                host->h_addr_list = NULL;                              \
90                                                                       \
91        /* copy the host name into the buffer */                       \
92        host->h_name = p;                                              \
93        strcpy (p, h->h_name);                                         \
94        p += strlen (h->h_name) + 1;                                   \
95        host->h_addrtype = h->h_addrtype;                              \
96        host->h_length = h->h_length;                                  \
97                                                                       \
98        /* copy the aliases/addresses into the buffer */               \
99        /* and assign pointers into the hostent */                     \
100        *p = 0;                                                        \
101        if (num_aliases) {                                             \
102                for (i = 0; i < num_aliases; i++) {                    \
103                        strcpy (p, h->h_aliases[i]);                   \
104                        host->h_aliases[i] = p;                        \
105                        p += strlen (h->h_aliases[i]);                 \
106                }                                                      \
107                host->h_aliases[num_aliases] = NULL;                   \
108        }                                                              \
109                                                                       \
110        if (num_addrs) {                                               \
111                for (i = 0; i < num_addrs; i++) {                      \
112                        memcpy (p, h->h_addr_list[i], h->h_length);    \
113                        host->h_addr_list[i] = p;                      \
114                        p += h->h_length;                              \
115                }                                                      \
116                host->h_addr_list[num_addrs] = NULL;                   \
117        }                                                              \
118} G_STMT_END
119
120
121#ifdef ENABLE_IPv6
122/* some helpful utils for IPv6 lookups */
123#define IPv6_BUFLEN_MIN  (sizeof (char *) * 3)
124
125static int
126ai_to_herr (int error)
127{
128        switch (error) {
129        case EAI_NONAME:
130        case EAI_FAIL:
131                return HOST_NOT_FOUND;
132                break;
133        case EAI_SERVICE:
134                return NO_DATA;
135                break;
136        case EAI_ADDRFAMILY:
137                return NO_ADDRESS;
138                break;
139        case EAI_NODATA:
140                return NO_DATA;
141                break;
142        case EAI_MEMORY:
143                return ENOMEM;
144                break;
145        case EAI_AGAIN:
146                return TRY_AGAIN;
147                break;
148        case EAI_SYSTEM:
149                return errno;
150                break;
151        default:
152                return NO_RECOVERY;
153                break;
154        }
155}
156#endif /* ENABLE_IPv6 */
157
158/**
159 * e_gethostbyname_r:
160 * @name: the host to resolve
161 * @host: a buffer pointing to a struct hostent to use for storage
162 * @buf: a buffer to use for hostname storage
163 * @buflen: the size of @buf
164 * @herr: a pointer to a variable to store an error code in
165 *
166 * Resolves the hostname @name, in a hopefully-reentrant fashion.
167 *
168 * Return value: 0 on success, ERANGE if @buflen is too small,
169 * "something else" otherwise (in which case *@herr will be set to
170 * one of the gethostbyname() error codes).
171 **/
172int
173e_gethostbyname_r (const char *name, struct hostent *host,
174                   char *buf, size_t buflen, int *herr)
175{
176#ifdef ENABLE_IPv6
177        struct addrinfo hints, *res;
178        int retval, len;
179        char *addr;
180       
181        memset (&hints, 0, sizeof (struct addrinfo));
182        hints.ai_flags = AI_CANONNAME;
183        hints.ai_family = PF_UNSPEC;
184        hints.ai_socktype = 0;
185        hints.ai_protocol = 0;
186       
187        if ((retval = getaddrinfo (name, NULL, &hints, &res)) != 0) {
188                *herr = ai_to_herr (retval);
189                return -1;
190        }
191       
192        len = strlen (res->ai_canonname);
193        if (buflen < IPv6_BUFLEN_MIN + len + 1 + res->ai_addrlen)
194                return ERANGE;
195       
196        /* h_name */
197        strcpy (buf, res->ai_canonname);
198        host->h_name = buf;
199        buf += len;
200       
201        /* h_aliases */
202        ((char **) buf)[0] = NULL;
203        host->h_aliases = (char **) buf;
204        buf += sizeof (char *);
205       
206        /* h_addrtype and h_length */
207        host->h_length = res->ai_addrlen;
208        if (res->ai_family == PF_INET6) {
209                host->h_addrtype = AF_INET6;
210               
211                addr = (char *) &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
212        } else {
213                host->h_addrtype = AF_INET;
214               
215                addr = (char *) &((struct sockaddr_in *) res->ai_addr)->sin_addr;
216        }
217       
218        memcpy (buf, addr, host->h_length);
219        addr = buf;
220        buf += host->h_length;
221       
222        /* h_addr_list */
223        ((char **) buf)[0] = addr;
224        ((char **) buf)[1] = NULL;
225        host->h_addr_list = (char **) buf;
226       
227        freeaddrinfo (res);
228       
229        return 0;
230#else /* No support for IPv6 addresses */
231#ifdef HAVE_GETHOSTBYNAME_R
232#ifdef GETHOSTBYNAME_R_FIVE_ARGS
233        if (gethostbyname_r (name, host, buf, buflen, herr))
234                return 0;
235        else
236                return errno;
237#else
238        struct hostent *hp;
239        int retval;
240       
241        retval = gethostbyname_r (name, host, buf, buflen, &hp, herr);
242        if (hp != NULL)
243                *herr = 0;
244        return retval;
245#endif
246#else /* No support for gethostbyname_r */
247        struct hostent *h;
248       
249        G_LOCK (gethost_mutex);
250       
251        h = gethostbyname (name);
252       
253        if (!h) {
254                *herr = h_errno;
255                G_UNLOCK (gethost_mutex);
256                return -1;
257        }
258       
259        GETHOST_PROCESS (h, host, buf, buflen, herr);
260       
261        G_UNLOCK (gethost_mutex);
262       
263        return 0;
264#endif /* HAVE_GETHOSTBYNAME_R */
265#endif /* ENABLE_IPv6 */
266}
267
268
269/**
270 * e_gethostbyaddr_r:
271 * @addr: the addr to resolve
272 * @len: address length
273 * @type: AF type
274 * @host: a buffer pointing to a struct hostent to use for storage
275 * @buf: a buffer to use for hostname storage
276 * @buflen: the size of @buf
277 * @herr: a pointer to a variable to store an error code in
278 *
279 * Resolves the address @addr, in a hopefully-reentrant fashion.
280 *
281 * Return value: 0 on success, ERANGE if @buflen is too small,
282 * "something else" otherwise (in which case *@herr will be set to
283 * one of the gethostbyaddr() error codes).
284 **/
285int
286e_gethostbyaddr_r (const char *addr, int len, int type, struct hostent *host,
287                   char *buf, size_t buflen, int *herr)
288{
289#ifdef ENABLE_IPv6
290        struct addrinfo hints, *res;
291        const char *name;
292        int retval, len;
293       
294        if ((name = inet_ntop (type, addr, buf, buflen)) == NULL) {
295                if (errno == ENOSPC)
296                        return ERANGE;
297               
298                return -1;
299        }
300       
301        memset (&hints, 0, sizeof (struct addrinfo));
302        hints.ai_flags = AI_CANONNAME;
303        hints.ai_family = type == AF_INET6 ? PF_INET6 : PF_INET;
304        hints.ai_socktype = 0;
305        hints.ai_protocol = 0;
306       
307        if ((retval = getaddrinfo (name, NULL, &hints, &res)) != 0) {
308                *herr = ai_to_herr (retval);
309                return -1;
310        }
311       
312        len = strlen (res->ai_canonname);
313        if (buflen < IPv6_BUFLEN_MIN + len + 1 + res->ai_addrlen)
314                return ERANGE;
315       
316        /* h_name */
317        strcpy (buf, res->ai_canonname);
318        host->h_name = buf;
319        buf += len;
320       
321        /* h_aliases */
322        ((char **) buf)[0] = NULL;
323        host->h_aliases = (char **) buf;
324        buf += sizeof (char *);
325       
326        /* h_addrtype and h_length */
327        host->h_length = res->ai_addrlen;
328        if (res->ai_family == PF_INET6) {
329                host->h_addrtype = AF_INET6;
330               
331                addr = (char *) &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
332        } else {
333                host->h_addrtype = AF_INET;
334               
335                addr = (char *) &((struct sockaddr_in *) res->ai_addr)->sin_addr;
336        }
337       
338        memcpy (buf, addr, host->h_length);
339        addr = buf;
340        buf += host->h_length;
341       
342        /* h_addr_list */
343        ((char **) buf)[0] = addr;
344        ((char **) buf)[1] = NULL;
345        host->h_addr_list = (char **) buf;
346       
347        freeaddrinfo (res);
348       
349        return 0;
350#else /* No support for IPv6 addresses */
351#ifdef HAVE_GETHOSTBYADDR_R
352#ifdef GETHOSTBYADDR_R_SEVEN_ARGS
353        if (gethostbyaddr_r (addr, len, type, host, buf, buflen, herr))
354                return 0;
355        else
356                return errno;
357#else
358        struct hostent *hp;
359        int retval;
360       
361        retval = gethostbyaddr_r (addr, len, type, host, buf, buflen, &hp, herr);
362        if (hp != NULL)
363                *herr = 0;
364        return retval;
365#endif
366#else /* No support for gethostbyaddr_r */
367        struct hostent *h;
368       
369        G_LOCK (gethost_mutex);
370       
371        h = gethostbyaddr (addr, len, type);
372       
373        if (!h) {
374                *herr = h_errno;
375                G_UNLOCK (gethost_mutex);
376                return -1;
377        }
378       
379        GETHOST_PROCESS (h, host, buf, buflen, herr);
380       
381        G_UNLOCK (gethost_mutex);
382       
383        return 0;
384#endif /* HAVE_GETHOSTBYADDR_R */
385#endif /* ENABLE_IPv6 */
386}
Note: See TracBrowser for help on using the repository browser.