Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / camel-net-utils.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  * Authors: Michael Zucchi <notzed@ximian.com>
4  *          Jeffrey Stedfast <fejj@ximian.com>
5  *          Chris Toshok <toshok@ximian.com>
6  *
7  * Copyright (C) 2004 Ximian Inc.
8  *
9  * This program is free software; you can redistribute it and/or 
10  * modify it under the terms of version 2 of the GNU Lesser General Public 
11  * License as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21  * USA
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <errno.h>
29 #include <pthread.h>
30 #include <stdio.h>
31
32 #ifndef G_OS_WIN32
33 #include <sys/poll.h>
34 #endif
35
36 #include <glib.h>
37 #include <glib/gi18n-lib.h>
38
39 #include <libedataserver/e-msgport.h>
40
41 #include "camel-exception.h"
42 #include "camel-net-utils.h"
43 #include "camel-operation.h"
44
45 #define d(x)
46
47 #ifdef G_OS_WIN32
48
49 typedef short in_port_t;
50
51 #undef gai_strerror
52 #define gai_strerror my_gai_strerror
53
54 /* gai_strerror() is implemented as an inline function in Microsoft's
55  * SDK, but mingw lacks that. So implement here. The EAI_* errors can
56  * be handled with the normal FormatMessage() API,
57  * i.e. g_win32_error_message().
58  */
59
60 static const char *
61 gai_strerror (int error_code)
62 {
63         gchar *msg = g_win32_error_message (error_code);
64         GQuark quark = g_quark_from_string (msg);
65         const gchar *retval = g_quark_to_string (quark);
66
67         g_free (msg);
68
69         return retval;
70 }
71
72 #endif
73
74 /* gethostbyname emulation code for emulating getaddrinfo code ...
75
76 This should probably go away */
77
78 #ifdef NEED_ADDRINFO
79
80 #if !defined (HAVE_GETHOSTBYNAME_R) || !defined (HAVE_GETHOSTBYADDR_R)
81 G_LOCK_DEFINE_STATIC (gethost_mutex);
82 #endif
83
84 #define ALIGN(x) (((x) + (sizeof (char *) - 1)) & ~(sizeof (char *) - 1))
85
86 #define GETHOST_PROCESS(h, host, buf, buflen, herr) G_STMT_START {     \
87         int num_aliases = 0, num_addrs = 0;                            \
88         int req_length;                                                \
89         char *p;                                                       \
90         int i;                                                         \
91                                                                        \
92         /* check to make sure we have enough room in our buffer */     \
93         req_length = 0;                                                \
94         if (h->h_aliases) {                                            \
95                 for (i = 0; h->h_aliases[i]; i++)                      \
96                         req_length += strlen (h->h_aliases[i]) + 1;    \
97                 num_aliases = i;                                       \
98         }                                                              \
99                                                                        \
100         if (h->h_addr_list) {                                          \
101                 for (i = 0; h->h_addr_list[i]; i++)                    \
102                         req_length += h->h_length;                     \
103                 num_addrs = i;                                         \
104         }                                                              \
105                                                                        \
106         req_length += sizeof (char *) * (num_aliases + 1);             \
107         req_length += sizeof (char *) * (num_addrs + 1);               \
108         req_length += strlen (h->h_name) + 1;                          \
109                                                                        \
110         if (buflen < req_length) {                                     \
111                 *herr = ERANGE;                                        \
112                 G_UNLOCK (gethost_mutex);                              \
113                 return ERANGE;                                         \
114         }                                                              \
115                                                                        \
116         /* we store the alias/addr pointers in the buffer */           \
117         /* their addresses here. */                                    \
118         p = buf;                                                       \
119         if (num_aliases) {                                             \
120                 host->h_aliases = (char **) p;                         \
121                 p += sizeof (char *) * (num_aliases + 1);              \
122         } else                                                         \
123                 host->h_aliases = NULL;                                \
124                                                                        \
125         if (num_addrs) {                                               \
126                 host->h_addr_list = (char **) p;                       \
127                 p += sizeof (char *) * (num_addrs + 1);                \
128         } else                                                         \
129                 host->h_addr_list = NULL;                              \
130                                                                        \
131         /* copy the host name into the buffer */                       \
132         host->h_name = p;                                              \
133         strcpy (p, h->h_name);                                         \
134         p += strlen (h->h_name) + 1;                                   \
135         host->h_addrtype = h->h_addrtype;                              \
136         host->h_length = h->h_length;                                  \
137                                                                        \
138         /* copy the aliases/addresses into the buffer */               \
139         /* and assign pointers into the hostent */                     \
140         *p = 0;                                                        \
141         if (num_aliases) {                                             \
142                 for (i = 0; i < num_aliases; i++) {                    \
143                         strcpy (p, h->h_aliases[i]);                   \
144                         host->h_aliases[i] = p;                        \
145                         p += strlen (h->h_aliases[i]);                 \
146                 }                                                      \
147                 host->h_aliases[num_aliases] = NULL;                   \
148         }                                                              \
149                                                                        \
150         if (num_addrs) {                                               \
151                 for (i = 0; i < num_addrs; i++) {                      \
152                         memcpy (p, h->h_addr_list[i], h->h_length);    \
153                         host->h_addr_list[i] = p;                      \
154                         p += h->h_length;                              \
155                 }                                                      \
156                 host->h_addr_list[num_addrs] = NULL;                   \
157         }                                                              \
158 } G_STMT_END
159
160
161 #ifdef ENABLE_IPv6
162 /* some helpful utils for IPv6 lookups */
163 #define IPv6_BUFLEN_MIN  (sizeof (char *) * 3)
164
165 static int
166 ai_to_herr (int error)
167 {
168         switch (error) {
169         case EAI_NONAME:
170         case EAI_FAIL:
171                 return HOST_NOT_FOUND;
172                 break;
173         case EAI_SERVICE:
174                 return NO_DATA;
175                 break;
176         case EAI_ADDRFAMILY:
177                 return NO_ADDRESS;
178                 break;
179         case EAI_NODATA:
180                 return NO_DATA;
181                 break;
182         case EAI_MEMORY:
183                 return ENOMEM;
184                 break;
185         case EAI_AGAIN:
186                 return TRY_AGAIN;
187                 break;
188         case EAI_SYSTEM:
189                 return errno;
190                 break;
191         default:
192                 return NO_RECOVERY;
193                 break;
194         }
195 }
196
197 #endif /* ENABLE_IPv6 */
198
199 static int
200 camel_gethostbyname_r (const char *name, struct hostent *host,
201                        char *buf, size_t buflen, int *herr)
202 {
203 #ifdef ENABLE_IPv6
204         struct addrinfo hints, *res;
205         int retval, len;
206         char *addr;
207         
208         memset (&hints, 0, sizeof (struct addrinfo));
209 #ifdef HAVE_AI_ADDRCONFIG
210         hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
211 #else
212         hints.ai_flags = AI_CANONNAME;
213 #endif
214         hints.ai_family = PF_UNSPEC;
215         hints.ai_socktype = SOCK_STREAM;
216         hints.ai_protocol = IPPROTO_TCP;
217         
218         if ((retval = getaddrinfo (name, NULL, &hints, &res)) != 0) {
219                 *herr = ai_to_herr (retval);
220                 return -1;
221         }
222         
223         len = ALIGN (strlen (res->ai_canonname) + 1);
224         if (buflen < IPv6_BUFLEN_MIN + len + res->ai_addrlen + sizeof (char *))
225                 return ERANGE;
226         
227         /* h_name */
228         strcpy (buf, res->ai_canonname);
229         host->h_name = buf;
230         buf += len;
231         
232         /* h_aliases */
233         ((char **) buf)[0] = NULL;
234         host->h_aliases = (char **) buf;
235         buf += sizeof (char *);
236         
237         /* h_addrtype and h_length */
238         host->h_length = res->ai_addrlen;
239         if (res->ai_family == PF_INET6) {
240                 host->h_addrtype = AF_INET6;
241                 
242                 addr = (char *) &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
243         } else {
244                 host->h_addrtype = AF_INET;
245                 
246                 addr = (char *) &((struct sockaddr_in *) res->ai_addr)->sin_addr;
247         }
248         
249         memcpy (buf, addr, host->h_length);
250         addr = buf;
251         buf += ALIGN (host->h_length);
252         
253         /* h_addr_list */
254         ((char **) buf)[0] = addr;
255         ((char **) buf)[1] = NULL;
256         host->h_addr_list = (char **) buf;
257         
258         freeaddrinfo (res);
259         
260         return 0;
261 #else /* No support for IPv6 addresses */
262 #ifdef HAVE_GETHOSTBYNAME_R
263 #ifdef GETHOSTBYNAME_R_FIVE_ARGS
264         if (gethostbyname_r (name, host, buf, buflen, herr))
265                 return 0;
266         else
267                 return errno;
268 #else
269         struct hostent *hp;
270         int retval;
271         
272         retval = gethostbyname_r (name, host, buf, buflen, &hp, herr);
273         if (hp != NULL) {
274                 *herr = 0;
275         } else if (retval == 0) {
276                 /* glibc 2.3.2 workaround - it seems that
277                  * gethostbyname_r will sometimes return 0 on fail and
278                  * not set the hostent values (hence the crash in bug
279                  * #56337).  Hopefully we can depend on @hp being NULL
280                  * in this error case like we do with
281                  * gethostbyaddr_r().
282                  */
283                 retval = -1;
284         }
285         
286         return retval;
287 #endif
288 #else /* No support for gethostbyname_r */
289         struct hostent *h;
290         
291         G_LOCK (gethost_mutex);
292         
293         h = gethostbyname (name);
294         
295         if (!h) {
296                 *herr = h_errno;
297                 G_UNLOCK (gethost_mutex);
298                 return -1;
299         }
300         
301         GETHOST_PROCESS (h, host, buf, buflen, herr);
302         
303         G_UNLOCK (gethost_mutex);
304         
305         return 0;
306 #endif /* HAVE_GETHOSTBYNAME_R */
307 #endif /* ENABLE_IPv6 */
308 }
309
310 static int
311 camel_gethostbyaddr_r (const char *addr, int addrlen, int type, struct hostent *host,
312                        char *buf, size_t buflen, int *herr)
313 {
314 #ifdef ENABLE_IPv6
315         int retval, len;
316         
317         if ((retval = getnameinfo (addr, addrlen, buf, buflen, NULL, 0, NI_NAMEREQD)) != 0) {
318                 *herr = ai_to_herr (retval);
319                 return -1;
320         }
321         
322         len = ALIGN (strlen (buf) + 1);
323         if (buflen < IPv6_BUFLEN_MIN + len + addrlen + sizeof (char *))
324                 return ERANGE;
325         
326         /* h_name */
327         host->h_name = buf;
328         buf += len;
329         
330         /* h_aliases */
331         ((char **) buf)[0] = NULL;
332         host->h_aliases = (char **) buf;
333         buf += sizeof (char *);
334         
335         /* h_addrtype and h_length */
336         host->h_length = addrlen;
337         host->h_addrtype = type;
338         
339         memcpy (buf, addr, host->h_length);
340         addr = buf;
341         buf += ALIGN (host->h_length);
342         
343         /* h_addr_list */
344         ((char **) buf)[0] = addr;
345         ((char **) buf)[1] = NULL;
346         host->h_addr_list = (char **) buf;
347         
348         return 0;
349 #else /* No support for IPv6 addresses */
350 #ifdef HAVE_GETHOSTBYADDR_R
351 #ifdef GETHOSTBYADDR_R_SEVEN_ARGS
352         if (gethostbyaddr_r (addr, addrlen, type, host, buf, buflen, herr))
353                 return 0;
354         else
355                 return errno;
356 #else
357         struct hostent *hp;
358         int retval;
359         
360         retval = gethostbyaddr_r (addr, addrlen, type, host, buf, buflen, &hp, herr);
361         if (hp != NULL) {
362                 *herr = 0;
363                 retval = 0;
364         } else if (retval == 0) {
365                 /* glibc 2.3.2 workaround - it seems that
366                  * gethostbyaddr_r will sometimes return 0 on fail and
367                  * fill @host with garbage strings from /etc/hosts
368                  * (failure to parse the file? who knows). Luckily, it
369                  * seems that we can rely on @hp being NULL on
370                  * fail.
371                  */
372                 retval = -1;
373         }
374         
375         return retval;
376 #endif
377 #else /* No support for gethostbyaddr_r */
378         struct hostent *h;
379         
380         G_LOCK (gethost_mutex);
381         
382         h = gethostbyaddr (addr, addrlen, type);
383         
384         if (!h) {
385                 *herr = h_errno;
386                 G_UNLOCK (gethost_mutex);
387                 return -1;
388         }
389         
390         GETHOST_PROCESS (h, host, buf, buflen, herr);
391         
392         G_UNLOCK (gethost_mutex);
393         
394         return 0;
395 #endif /* HAVE_GETHOSTBYADDR_R */
396 #endif /* ENABLE_IPv6 */
397 }
398 #endif /* NEED_ADDRINFO */
399
400 /* ********************************************************************** */
401 struct _addrinfo_msg {
402         EMsg msg;
403         unsigned int cancelled:1;
404
405         /* for host lookup */
406         const char *name;
407         const char *service;
408         int result;
409         const struct addrinfo *hints;
410         struct addrinfo **res;
411
412         /* for host lookup emulation */
413 #ifdef NEED_ADDRINFO
414         struct hostent hostbuf;
415         int hostbuflen;
416         char *hostbufmem;
417 #endif
418
419         /* for name lookup */
420         const struct sockaddr *addr;
421         socklen_t addrlen;
422         char *host;
423         int hostlen;
424         char *serv;
425         int servlen;
426         int flags;
427 };
428
429 static void
430 cs_freeinfo(struct _addrinfo_msg *msg)
431 {
432         g_free(msg->host);
433         g_free(msg->serv);
434 #ifdef NEED_ADDRINFO
435         g_free(msg->hostbufmem);
436 #endif
437         g_free(msg);
438 }
439
440 /* returns -1 if we didn't wait for reply from thread */
441 static int
442 cs_waitinfo(void *(worker)(void *), struct _addrinfo_msg *msg, const char *error, CamelException *ex)
443 {
444         EMsgPort *reply_port;
445         pthread_t id;
446         int err, cancel_fd, cancel = 0, fd;
447
448         cancel_fd = camel_operation_cancel_fd(NULL);
449         if (cancel_fd == -1) {
450                 worker(msg);
451                 return 0;
452         }
453         
454         reply_port = msg->msg.reply_port = e_msgport_new();
455         fd = e_msgport_fd(msg->msg.reply_port);
456         if ((err = pthread_create(&id, NULL, worker, msg)) == 0) {
457                 int status;
458 #ifndef G_OS_WIN32
459                 struct pollfd polls[2];
460
461                 polls[0].fd = fd;
462                 polls[0].events = POLLIN;
463                 polls[1].fd = cancel_fd;
464                 polls[1].events = POLLIN;
465
466                 d(printf("waiting for name return/cancellation in main process\n"));
467                 do {
468                         polls[0].revents = 0;
469                         polls[1].revents = 0;
470                         status = poll(polls, 2, -1);
471                 } while (status == -1 && errno == EINTR);
472 #else
473                 fd_set read_set;
474
475                 FD_ZERO(&read_set);
476                 FD_SET(fd, &read_set);
477                 FD_SET(cancel_fd, &read_set);
478
479                 status = select(MAX(fd, cancel_fd) + 1, &read_set, NULL, NULL, NULL);
480 #endif
481
482                 if (status == -1 ||
483 #ifndef G_OS_WIN32
484                     (polls[1].revents & POLLIN)
485 #else
486                     FD_ISSET (cancel_fd, &read_set)
487 #endif
488                                                    ) {
489                         if (status == -1)
490                                 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, "%s: %s", error,
491 #ifndef G_OS_WIN32
492                                                      g_strerror(errno)
493 #else
494                                                      g_win32_error_message (WSAGetLastError ())
495 #endif
496                                                      );
497                         else
498                                 camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("Canceled"));
499                         
500                         /* We cancel so if the thread impl is decent it causes immediate exit.
501                            We detach so we dont need to wait for it to exit if it isn't.
502                            We check the reply port incase we had a reply in the mean time, which we free later */
503                         d(printf("Canceling lookup thread and leaving it\n"));
504                         msg->cancelled = 1;
505                         pthread_detach(id);
506                         pthread_cancel(id);
507                         cancel = 1;
508                 } else {
509                         struct _addrinfo_msg *reply = (struct _addrinfo_msg *)e_msgport_get(reply_port);
510
511                         g_assert(reply == msg);
512                         d(printf("waiting for child to exit\n"));
513                         pthread_join(id, NULL);
514                         d(printf("child done\n"));
515                 }
516         } else {
517                 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, "%s: %s: %s", error, _("cannot create thread"), g_strerror(err));
518         }
519         e_msgport_destroy(reply_port);
520
521         return cancel;
522 }
523
524 #ifdef NEED_ADDRINFO
525 static void *
526 cs_getaddrinfo(void *data)
527 {
528         struct _addrinfo_msg *msg = data;
529         int herr;
530         struct hostent h;
531         struct addrinfo *res, *last = NULL;
532         struct sockaddr_in *sin;
533         in_port_t port = 0;
534         int i;
535
536         /* This is a pretty simplistic emulation of getaddrinfo */
537
538         while ((msg->result = camel_gethostbyname_r(msg->name, &h, msg->hostbufmem, msg->hostbuflen, &herr)) == ERANGE) {
539                 pthread_testcancel();
540                 msg->hostbuflen *= 2;
541                 msg->hostbufmem = g_realloc(msg->hostbufmem, msg->hostbuflen);
542         }
543         
544         /* If we got cancelled, dont reply, just free it */
545         if (msg->cancelled)
546                 goto cancel;
547
548         /* FIXME: map error numbers across */
549         if (msg->result != 0)
550                 goto reply;
551
552         /* check hints matched */
553         if (msg->hints && msg->hints->ai_family && msg->hints->ai_family != h.h_addrtype) {
554                 msg->result = EAI_FAMILY;
555                 goto reply;
556         }
557
558         /* we only support ipv4 for this interface, even if it could supply ipv6 */
559         if (h.h_addrtype != AF_INET) {
560                 msg->result = EAI_FAMILY;
561                 goto reply;
562         }
563
564         /* check service mapping */
565         if (msg->service) {
566                 const char *p = msg->service;
567
568                 while (*p) {
569                         if (*p < '0' || *p > '9')
570                                 break;
571                         p++;
572                 }
573
574                 if (*p) {
575                         const char *socktype = NULL;
576                         struct servent *serv;
577
578                         if (msg->hints && msg->hints->ai_socktype) {
579                                 if (msg->hints->ai_socktype == SOCK_STREAM)
580                                         socktype = "tcp";
581                                 else if (msg->hints->ai_socktype == SOCK_DGRAM)
582                                         socktype = "udp";
583                         }
584
585                         serv = getservbyname(msg->service, socktype);
586                         if (serv == NULL) {
587                                 msg->result = EAI_NONAME;
588                                 goto reply;
589                         }
590                         port = serv->s_port;
591                 } else {
592                         port = htons(strtoul(msg->service, NULL, 10));
593                 }
594         }
595
596         for (i=0;h.h_addr_list[i];i++) {
597                 res = g_malloc0(sizeof(*res));
598                 if (msg->hints) {
599                         res->ai_flags = msg->hints->ai_flags;
600                         if (msg->hints->ai_flags & AI_CANONNAME)
601                                 res->ai_canonname = g_strdup(h.h_name);
602                         res->ai_socktype = msg->hints->ai_socktype;
603                         res->ai_protocol = msg->hints->ai_protocol;
604                 } else {
605                         res->ai_flags = 0;
606                         res->ai_socktype = SOCK_STREAM; /* fudge */
607                         res->ai_protocol = 0;   /* fudge */
608                 }
609                 res->ai_family = AF_INET;
610                 res->ai_addrlen = sizeof(*sin);
611                 res->ai_addr = g_malloc(sizeof(*sin));
612                 sin = (struct sockaddr_in *)res->ai_addr;
613                 sin->sin_family = AF_INET;
614                 sin->sin_port = port;
615                 memcpy(&sin->sin_addr, h.h_addr_list[i], sizeof(sin->sin_addr));
616
617                 if (last == NULL) {
618                         *msg->res = last = res;
619                 } else {
620                         last->ai_next = res;
621                         last = res;
622                 }
623         }
624 reply:
625         e_msgport_reply((EMsg *)msg);
626         return NULL;
627 cancel:
628         cs_freeinfo(msg);
629         return NULL;
630 }
631 #else
632 static void *
633 cs_getaddrinfo(void *data)
634 {
635         struct _addrinfo_msg *info = data;
636
637         info->result = getaddrinfo(info->name, info->service, info->hints, info->res);
638         
639         if (info->cancelled) {
640                 cs_freeinfo(info);
641         } else {
642                 e_msgport_reply((EMsg *)info);
643         }
644         
645         return NULL;
646 }
647 #endif /* NEED_ADDRINFO */
648
649 struct addrinfo *
650 camel_getaddrinfo(const char *name, const char *service, const struct addrinfo *hints, CamelException *ex)
651 {
652         struct _addrinfo_msg *msg;
653         struct addrinfo *res = NULL;
654 #ifndef ENABLE_IPv6
655         struct addrinfo myhints;
656 #endif
657         g_return_val_if_fail(name != NULL, NULL);
658         
659         if (camel_operation_cancel_check(NULL)) {
660                 camel_exception_set(ex, CAMEL_EXCEPTION_USER_CANCEL, _("Canceled"));
661                 return NULL;
662         }
663
664         camel_operation_start_transient(NULL, _("Resolving: %s"), name);
665
666         /* force ipv4 addresses only */
667 #ifndef ENABLE_IPv6
668         if (hints == NULL)
669                 memset(&myhints, 0, sizeof(myhints));
670         else
671                 memcpy (&myhints, hints, sizeof (myhints));
672         
673         myhints.ai_family = AF_INET;
674         hints = &myhints;
675 #endif
676
677         msg = g_malloc0(sizeof(*msg));
678         msg->name = name;
679         msg->service = service;
680         msg->hints = hints;
681         msg->res = &res;
682 #ifdef NEED_ADDRINFO
683         msg->hostbuflen = 1024;
684         msg->hostbufmem = g_malloc(msg->hostbuflen);
685 #endif  
686         if (cs_waitinfo(cs_getaddrinfo, msg, _("Host lookup failed"), ex) == 0) {
687                 if (msg->result != 0)
688                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Host lookup failed: %s: %s"),
689                                               name, gai_strerror (msg->result));
690                 
691                 cs_freeinfo(msg);
692         } else
693                 res = NULL;
694         
695         camel_operation_end(NULL);
696
697         return res;
698 }
699
700 void
701 camel_freeaddrinfo(struct addrinfo *host)
702 {
703 #ifdef NEED_ADDRINFO
704         while (host) {
705                 struct addrinfo *next = host->ai_next;
706
707                 g_free(host->ai_canonname);
708                 g_free(host->ai_addr);
709                 g_free(host);
710                 host = next;
711         }
712 #else
713         freeaddrinfo(host);
714 #endif
715 }
716
717 #ifdef NEED_ADDRINFO
718 static void *
719 cs_getnameinfo(void *data)
720 {
721         struct _addrinfo_msg *msg = data;
722         int herr;
723         struct hostent h;
724         struct sockaddr_in *sin = (struct sockaddr_in *)msg->addr;
725
726         /* FIXME: error code */
727         if (msg->addr->sa_family != AF_INET) {
728                 msg->result = -1;
729                 return NULL;
730         }
731
732         /* FIXME: honour getnameinfo flags: do we care, not really */
733
734         while ((msg->result = camel_gethostbyaddr_r((const char *)&sin->sin_addr, sizeof(sin->sin_addr), AF_INET, &h,
735                                                     msg->hostbufmem, msg->hostbuflen, &herr)) == ERANGE) {
736                 pthread_testcancel ();
737                 msg->hostbuflen *= 2;
738                 msg->hostbufmem = g_realloc(msg->hostbufmem, msg->hostbuflen);
739         }
740         
741         if (msg->cancelled)
742                 goto cancel;
743
744         if (msg->host) {
745                 g_free(msg->host);
746                 if (msg->result == 0 && h.h_name && h.h_name[0]) {
747                         msg->host = g_strdup(h.h_name);
748                 } else {
749                         unsigned char *in = (unsigned char *)&sin->sin_addr;
750                         
751                         /* sin_addr is always network order which is big-endian */
752                         msg->host = g_strdup_printf("%u.%u.%u.%u", in[0], in[1], in[2], in[3]);
753                 }
754         }
755
756         /* we never actually use this anyway */
757         if (msg->serv)
758                 sprintf(msg->serv, "%d", sin->sin_port);
759
760         e_msgport_reply((EMsg *)msg);
761         return NULL;
762 cancel:
763         cs_freeinfo(msg);
764         return NULL;
765 }
766 #else
767 static void *
768 cs_getnameinfo(void *data)
769 {
770         struct _addrinfo_msg *msg = data;
771
772         /* there doens't appear to be a return code which says host or serv buffers are too short, lengthen them */
773         msg->result = getnameinfo(msg->addr, msg->addrlen, msg->host, msg->hostlen, msg->serv, msg->servlen, msg->flags);
774         
775         if (msg->cancelled)
776                 cs_freeinfo(msg);
777         else
778                 e_msgport_reply((EMsg *)msg);
779
780         return NULL;
781 }
782 #endif
783
784 int
785 camel_getnameinfo(const struct sockaddr *sa, socklen_t salen, char **host, char **serv, int flags, CamelException *ex)
786 {
787         struct _addrinfo_msg *msg;
788         int result;
789
790         if (camel_operation_cancel_check(NULL)) {
791                 camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Canceled"));
792                 return -1;
793         }
794
795         camel_operation_start_transient(NULL, _("Resolving address"));
796
797         msg = g_malloc0(sizeof(*msg));
798         msg->addr = sa;
799         msg->addrlen = salen;
800         if (host) {
801                 msg->hostlen = NI_MAXHOST;
802                 msg->host = g_malloc(msg->hostlen);
803                 msg->host[0] = 0;
804         }
805         if (serv) {
806                 msg->servlen = NI_MAXSERV;
807                 msg->serv = g_malloc(msg->servlen);
808                 msg->serv[0] = 0;
809         }
810         msg->flags = flags;
811 #ifdef NEED_ADDRINFO
812         msg->hostbuflen = 1024;
813         msg->hostbufmem = g_malloc(msg->hostbuflen);
814 #endif
815         cs_waitinfo(cs_getnameinfo, msg, _("Name lookup failed"), ex);
816
817         if ((result = msg->result) != 0)
818                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Name lookup failed: %s"),
819                                       gai_strerror (result));
820         else {
821                 if (host)
822                         *host = g_strdup(msg->host);
823                 if (serv)
824                         *serv = g_strdup(msg->serv);
825         }
826
827         g_free(msg->host);
828         g_free(msg->serv);
829         g_free(msg);
830
831         camel_operation_end(NULL);
832
833         return result;
834 }