From c83477345fa5dceb62b85efd0a76a54f94a78858 Mon Sep 17 00:00:00 2001 From: Not Zed Date: Fri, 3 Dec 2004 03:36:57 +0000 Subject: have our own so we don't need to link with glib. * camel-lock-helper.c (g_strerror): have our own so we don't need to link with glib. * providers/*/Makefile.am: Do not install ANY provider header files. No providers are subclassable. No providers are directly linkable. * camel.pc.in: create package config file. * tests/lib/folders.c (test_folder_message_ops): updated counts for delete also marking unread. * tests/lib/camel-test.c (camel_test_provider_init): new api for initialising test system 'in-build'. * camel-provider.c: remove the assertions, init if we need to,k use pthread_once stuff to serialise it. * tests/folder/test3.c (main): remove gtk stuff (???). * tests/*: Fix all the makefiles. Made make-check work 'in-build'. * tests/lib/folders.c (test_folder_counts): update for api changes. (test_message_info): similar. * providers/Makefile.am: removed groupwise from the build, this can't go in here anymore, not in its current state. * camel-net-utils.c (camel_gethostbyaddr_r) (camel_gethostbyname_r): the old e_gethost* calls from e-host-utils.c. 2004-11-15 Not Zed * providers/imap/camel-imap-utils.c (imap_path_to_physical): copied from e-path.c. (imap_path_find_folders): copied from e-path.c. * camel.h: remove the provider stuff from the header. * camel-provider.c: globalise provider_init variable, and asserton it in all functions that rely on it. * camel-service.c: removed getaddrinfo/etc. * camel-net-utils.[ch]: separate out camel_getaddrinfo etc. * Makefile.am: split camel into 2 libraries, libcamel and libcamel-store. * camel-multipart-signed.c (camel_multipart_signed_sign) (camel_multipart_signed_verify, prepare_sign): remove old deprecated api. * camel-multipart-encrypted.c (camel_multipart_encrypted_encrypt) (camel_multipart_encrypted_decrypt): remove old deprecated api. svn path=/trunk/; revision=28046 --- camel/camel-net-utils.c | 781 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 781 insertions(+) create mode 100644 camel/camel-net-utils.c (limited to 'camel/camel-net-utils.c') diff --git a/camel/camel-net-utils.c b/camel/camel-net-utils.c new file mode 100644 index 0000000000..a4d8159389 --- /dev/null +++ b/camel/camel-net-utils.c @@ -0,0 +1,781 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Authors: Michael Zucchi + * Jeffrey Stedfast + * Chris Toshok + * + * Copyright (C) 2004 Ximian Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include + +#include "camel-i18n.h" +#include "camel-operation.h" +#include "camel-exception.h" +#include "camel-net-utils.h" + +#include "libedataserver/e-msgport.h" + +#define d(x) + +/* gethostbyname emulation code for emulating getaddrinfo code ... + +This should probably go away */ + +#ifdef NEED_ADDRINFO + +#if !defined (HAVE_GETHOSTBYNAME_R) || !defined (HAVE_GETHOSTBYADDR_R) +G_LOCK_DEFINE_STATIC (gethost_mutex); +#endif + +#define ALIGN(x) (((x) + (sizeof (char *) - 1)) & ~(sizeof (char *) - 1)) + +#define GETHOST_PROCESS(h, host, buf, buflen, herr) G_STMT_START { \ + int num_aliases = 0, num_addrs = 0; \ + int req_length; \ + char *p; \ + int i; \ + \ + /* check to make sure we have enough room in our buffer */ \ + req_length = 0; \ + if (h->h_aliases) { \ + for (i = 0; h->h_aliases[i]; i++) \ + req_length += strlen (h->h_aliases[i]) + 1; \ + num_aliases = i; \ + } \ + \ + if (h->h_addr_list) { \ + for (i = 0; h->h_addr_list[i]; i++) \ + req_length += h->h_length; \ + num_addrs = i; \ + } \ + \ + req_length += sizeof (char *) * (num_aliases + 1); \ + req_length += sizeof (char *) * (num_addrs + 1); \ + req_length += strlen (h->h_name) + 1; \ + \ + if (buflen < req_length) { \ + *herr = ERANGE; \ + G_UNLOCK (gethost_mutex); \ + return ERANGE; \ + } \ + \ + /* we store the alias/addr pointers in the buffer */ \ + /* their addresses here. */ \ + p = buf; \ + if (num_aliases) { \ + host->h_aliases = (char **) p; \ + p += sizeof (char *) * (num_aliases + 1); \ + } else \ + host->h_aliases = NULL; \ + \ + if (num_addrs) { \ + host->h_addr_list = (char **) p; \ + p += sizeof (char *) * (num_addrs + 1); \ + } else \ + host->h_addr_list = NULL; \ + \ + /* copy the host name into the buffer */ \ + host->h_name = p; \ + strcpy (p, h->h_name); \ + p += strlen (h->h_name) + 1; \ + host->h_addrtype = h->h_addrtype; \ + host->h_length = h->h_length; \ + \ + /* copy the aliases/addresses into the buffer */ \ + /* and assign pointers into the hostent */ \ + *p = 0; \ + if (num_aliases) { \ + for (i = 0; i < num_aliases; i++) { \ + strcpy (p, h->h_aliases[i]); \ + host->h_aliases[i] = p; \ + p += strlen (h->h_aliases[i]); \ + } \ + host->h_aliases[num_aliases] = NULL; \ + } \ + \ + if (num_addrs) { \ + for (i = 0; i < num_addrs; i++) { \ + memcpy (p, h->h_addr_list[i], h->h_length); \ + host->h_addr_list[i] = p; \ + p += h->h_length; \ + } \ + host->h_addr_list[num_addrs] = NULL; \ + } \ +} G_STMT_END + + +#ifdef ENABLE_IPv6 +/* some helpful utils for IPv6 lookups */ +#define IPv6_BUFLEN_MIN (sizeof (char *) * 3) + +static int +ai_to_herr (int error) +{ + switch (error) { + case EAI_NONAME: + case EAI_FAIL: + return HOST_NOT_FOUND; + break; + case EAI_SERVICE: + return NO_DATA; + break; + case EAI_ADDRFAMILY: + return NO_ADDRESS; + break; + case EAI_NODATA: + return NO_DATA; + break; + case EAI_MEMORY: + return ENOMEM; + break; + case EAI_AGAIN: + return TRY_AGAIN; + break; + case EAI_SYSTEM: + return errno; + break; + default: + return NO_RECOVERY; + break; + } +} + +#endif /* ENABLE_IPv6 */ + +static int +camel_gethostbyname_r (const char *name, struct hostent *host, + char *buf, size_t buflen, int *herr) +{ +#ifdef ENABLE_IPv6 + struct addrinfo hints, *res; + int retval, len; + char *addr; + + memset (&hints, 0, sizeof (struct addrinfo)); +#ifdef HAVE_AI_ADDRCONFIG + hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; +#else + hints.ai_flags = AI_CANONNAME; +#endif + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if ((retval = getaddrinfo (name, NULL, &hints, &res)) != 0) { + *herr = ai_to_herr (retval); + return -1; + } + + len = ALIGN (strlen (res->ai_canonname) + 1); + if (buflen < IPv6_BUFLEN_MIN + len + res->ai_addrlen + sizeof (char *)) + return ERANGE; + + /* h_name */ + strcpy (buf, res->ai_canonname); + host->h_name = buf; + buf += len; + + /* h_aliases */ + ((char **) buf)[0] = NULL; + host->h_aliases = (char **) buf; + buf += sizeof (char *); + + /* h_addrtype and h_length */ + host->h_length = res->ai_addrlen; + if (res->ai_family == PF_INET6) { + host->h_addrtype = AF_INET6; + + addr = (char *) &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr; + } else { + host->h_addrtype = AF_INET; + + addr = (char *) &((struct sockaddr_in *) res->ai_addr)->sin_addr; + } + + memcpy (buf, addr, host->h_length); + addr = buf; + buf += ALIGN (host->h_length); + + /* h_addr_list */ + ((char **) buf)[0] = addr; + ((char **) buf)[1] = NULL; + host->h_addr_list = (char **) buf; + + freeaddrinfo (res); + + return 0; +#else /* No support for IPv6 addresses */ +#ifdef HAVE_GETHOSTBYNAME_R +#ifdef GETHOSTBYNAME_R_FIVE_ARGS + if (gethostbyname_r (name, host, buf, buflen, herr)) + return 0; + else + return errno; +#else + struct hostent *hp; + int retval; + + retval = gethostbyname_r (name, host, buf, buflen, &hp, herr); + if (hp != NULL) { + *herr = 0; + } else if (retval == 0) { + /* glibc 2.3.2 workaround - it seems that + * gethostbyname_r will sometimes return 0 on fail and + * not set the hostent values (hence the crash in bug + * #56337). Hopefully we can depend on @hp being NULL + * in this error case like we do with + * gethostbyaddr_r(). + */ + retval = -1; + } + + return retval; +#endif +#else /* No support for gethostbyname_r */ + struct hostent *h; + + G_LOCK (gethost_mutex); + + h = gethostbyname (name); + + if (!h) { + *herr = h_errno; + G_UNLOCK (gethost_mutex); + return -1; + } + + GETHOST_PROCESS (h, host, buf, buflen, herr); + + G_UNLOCK (gethost_mutex); + + return 0; +#endif /* HAVE_GETHOSTBYNAME_R */ +#endif /* ENABLE_IPv6 */ +} + +static int +camel_gethostbyaddr_r (const char *addr, int addrlen, int type, struct hostent *host, + char *buf, size_t buflen, int *herr) +{ +#ifdef ENABLE_IPv6 + int retval, len; + + if ((retval = getnameinfo (addr, addrlen, buf, buflen, NULL, 0, NI_NAMEREQD)) != 0) { + *herr = ai_to_herr (retval); + return -1; + } + + len = ALIGN (strlen (buf) + 1); + if (buflen < IPv6_BUFLEN_MIN + len + addrlen + sizeof (char *)) + return ERANGE; + + /* h_name */ + host->h_name = buf; + buf += len; + + /* h_aliases */ + ((char **) buf)[0] = NULL; + host->h_aliases = (char **) buf; + buf += sizeof (char *); + + /* h_addrtype and h_length */ + host->h_length = addrlen; + host->h_addrtype = type; + + memcpy (buf, addr, host->h_length); + addr = buf; + buf += ALIGN (host->h_length); + + /* h_addr_list */ + ((char **) buf)[0] = addr; + ((char **) buf)[1] = NULL; + host->h_addr_list = (char **) buf; + + return 0; +#else /* No support for IPv6 addresses */ +#ifdef HAVE_GETHOSTBYADDR_R +#ifdef GETHOSTBYADDR_R_SEVEN_ARGS + if (gethostbyaddr_r (addr, addrlen, type, host, buf, buflen, herr)) + return 0; + else + return errno; +#else + struct hostent *hp; + int retval; + + retval = gethostbyaddr_r (addr, addrlen, type, host, buf, buflen, &hp, herr); + if (hp != NULL) { + *herr = 0; + retval = 0; + } else if (retval == 0) { + /* glibc 2.3.2 workaround - it seems that + * gethostbyaddr_r will sometimes return 0 on fail and + * fill @host with garbage strings from /etc/hosts + * (failure to parse the file? who knows). Luckily, it + * seems that we can rely on @hp being NULL on + * fail. + */ + retval = -1; + } + + return retval; +#endif +#else /* No support for gethostbyaddr_r */ + struct hostent *h; + + G_LOCK (gethost_mutex); + + h = gethostbyaddr (addr, addrlen, type); + + if (!h) { + *herr = h_errno; + G_UNLOCK (gethost_mutex); + return -1; + } + + GETHOST_PROCESS (h, host, buf, buflen, herr); + + G_UNLOCK (gethost_mutex); + + return 0; +#endif /* HAVE_GETHOSTBYADDR_R */ +#endif /* ENABLE_IPv6 */ +} +#endif /* NEED_ADDRINFO */ + +/* ********************************************************************** */ +struct _addrinfo_msg { + EMsg msg; + unsigned int cancelled:1; + + /* for host lookup */ + const char *name; + const char *service; + int result; + const struct addrinfo *hints; + struct addrinfo **res; + + /* for host lookup emulation */ +#ifdef NEED_ADDRINFO + struct hostent hostbuf; + int hostbuflen; + char *hostbufmem; +#endif + + /* for name lookup */ + const struct sockaddr *addr; + socklen_t addrlen; + char *host; + int hostlen; + char *serv; + int servlen; + int flags; +}; + +static void +cs_freeinfo(struct _addrinfo_msg *msg) +{ + g_free(msg->host); + g_free(msg->serv); +#ifdef NEED_ADDRINFO + g_free(msg->hostbufmem); +#endif + g_free(msg); +} + +/* returns -1 if cancelled */ +static int +cs_waitinfo(void *(worker)(void *), struct _addrinfo_msg *msg, const char *error, CamelException *ex) +{ + EMsgPort *reply_port; + pthread_t id; + int err, cancel_fd, cancel = 0, fd; + + cancel_fd = camel_operation_cancel_fd(NULL); + if (cancel_fd == -1) { + worker(msg); + return 0; + } + + reply_port = msg->msg.reply_port = e_msgport_new(); + fd = e_msgport_fd(msg->msg.reply_port); + if ((err = pthread_create(&id, NULL, worker, msg)) == 0) { + struct pollfd polls[2]; + int status; + + polls[0].fd = fd; + polls[0].events = POLLIN; + polls[1].fd = cancel_fd; + polls[1].events = POLLIN; + + d(printf("waiting for name return/cancellation in main process\n")); + do { + polls[0].revents = 0; + polls[1].revents = 0; + status = poll(polls, 2, -1); + } while (status == -1 && errno == EINTR); + + if (status == -1 || (polls[1].revents & POLLIN)) { + if (status == -1) + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, "%s: %s", error, g_strerror(errno)); + else + camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("Cancelled")); + + /* We cancel so if the thread impl is decent it causes immediate exit. + We detach so we dont need to wait for it to exit if it isn't. + We check the reply port incase we had a reply in the mean time, which we free later */ + d(printf("Cancelling lookup thread and leaving it\n")); + msg->cancelled = 1; + pthread_detach(id); + pthread_cancel(id); + cancel = 1; + } else { + struct _addrinfo_msg *reply = (struct _addrinfo_msg *)e_msgport_get(reply_port); + + g_assert(reply == msg); + d(printf("waiting for child to exit\n")); + pthread_join(id, NULL); + d(printf("child done\n")); + } + } else { + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, "%s: %s: %s", error, _("cannot create thread"), g_strerror(err)); + } + e_msgport_destroy(reply_port); + + return cancel; +} + +#ifdef NEED_ADDRINFO +static void * +cs_getaddrinfo(void *data) +{ + struct _addrinfo_msg *msg = data; + int herr; + struct hostent h; + struct addrinfo *res, *last = NULL; + struct sockaddr_in *sin; + in_port_t port = 0; + int i; + + /* This is a pretty simplistic emulation of getaddrinfo */ + + while ((msg->result = camel_gethostbyname_r(msg->name, &h, msg->hostbufmem, msg->hostbuflen, &herr)) == ERANGE) { + pthread_testcancel(); + msg->hostbuflen *= 2; + msg->hostbufmem = g_realloc(msg->hostbufmem, msg->hostbuflen); + } + + /* If we got cancelled, dont reply, just free it */ + if (msg->cancelled) + goto cancel; + + /* FIXME: map error numbers across */ + if (msg->result != 0) + goto reply; + + /* check hints matched */ + if (msg->hints && msg->hints->ai_family && msg->hints->ai_family != h.h_addrtype) { + msg->result = EAI_FAMILY; + goto reply; + } + + /* we only support ipv4 for this interface, even if it could supply ipv6 */ + if (h.h_addrtype != AF_INET) { + msg->result = EAI_FAMILY; + goto reply; + } + + /* check service mapping */ + if (msg->service) { + const char *p = msg->service; + + while (*p) { + if (*p < '0' || *p > '9') + break; + p++; + } + + if (*p) { + const char *socktype = NULL; + struct servent *serv; + + if (msg->hints && msg->hints->ai_socktype) { + if (msg->hints->ai_socktype == SOCK_STREAM) + socktype = "tcp"; + else if (msg->hints->ai_socktype == SOCK_DGRAM) + socktype = "udp"; + } + + serv = getservbyname(msg->service, socktype); + if (serv == NULL) { + msg->result = EAI_NONAME; + goto reply; + } + port = serv->s_port; + } else { + port = htons(strtoul(msg->service, NULL, 10)); + } + } + + for (i=0;h.h_addr_list[i];i++) { + res = g_malloc0(sizeof(*res)); + if (msg->hints) { + res->ai_flags = msg->hints->ai_flags; + if (msg->hints->ai_flags & AI_CANONNAME) + res->ai_canonname = g_strdup(h.h_name); + res->ai_socktype = msg->hints->ai_socktype; + res->ai_protocol = msg->hints->ai_protocol; + } else { + res->ai_flags = 0; + res->ai_socktype = SOCK_STREAM; /* fudge */ + res->ai_protocol = 0; /* fudge */ + } + res->ai_family = AF_INET; + res->ai_addrlen = sizeof(*sin); + res->ai_addr = g_malloc(sizeof(*sin)); + sin = (struct sockaddr_in *)res->ai_addr; + sin->sin_family = AF_INET; + sin->sin_port = port; + memcpy(&sin->sin_addr, h.h_addr_list[i], sizeof(sin->sin_addr)); + + if (last == NULL) { + *msg->res = last = res; + } else { + last->ai_next = res; + last = res; + } + } +reply: + e_msgport_reply((EMsg *)msg); + return NULL; +cancel: + cs_freeinfo(msg); + return NULL; +} +#else +static void * +cs_getaddrinfo(void *data) +{ + struct _addrinfo_msg *info = data; + + info->result = getaddrinfo(info->name, info->service, info->hints, info->res); + + if (info->cancelled) { + g_free(info); + } else { + e_msgport_reply((EMsg *)info); + } + + return NULL; +} +#endif /* NEED_ADDRINFO */ + +struct addrinfo * +camel_getaddrinfo(const char *name, const char *service, const struct addrinfo *hints, CamelException *ex) +{ + struct _addrinfo_msg *msg; + struct addrinfo *res = NULL; +#ifndef ENABLE_IPv6 + struct addrinfo myhints; +#endif + g_return_val_if_fail(name != NULL, NULL); + + if (camel_operation_cancel_check(NULL)) { + camel_exception_set(ex, CAMEL_EXCEPTION_USER_CANCEL, _("Cancelled")); + return NULL; + } + + camel_operation_start_transient(NULL, _("Resolving: %s"), name); + + /* force ipv4 addresses only */ +#ifndef ENABLE_IPv6 + if (hints == NULL) + memset(&myhints, 0, sizeof(myhints)); + else + memcpy (&myhints, hints, sizeof (myhints)); + + myhints.ai_family = AF_INET; + hints = &myhints; +#endif + + msg = g_malloc0(sizeof(*msg)); + msg->name = name; + msg->service = service; + msg->hints = hints; + msg->res = &res; +#ifdef NEED_ADDRINFO + msg->hostbuflen = 1024; + msg->hostbufmem = g_malloc(msg->hostbuflen); +#endif + if (cs_waitinfo(cs_getaddrinfo, msg, _("Host lookup failed"), ex) == 0) { + if (msg->result != 0) + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Host lookup failed: %s: %s"), + name, gai_strerror (msg->result)); + + cs_freeinfo(msg); + } else + res = NULL; + + camel_operation_end(NULL); + + return res; +} + +void +camel_freeaddrinfo(struct addrinfo *host) +{ +#ifdef NEED_ADDRINFO + while (host) { + struct addrinfo *next = host->ai_next; + + g_free(host->ai_canonname); + g_free(host->ai_addr); + g_free(host); + host = next; + } +#else + freeaddrinfo(host); +#endif +} + +#ifdef NEED_ADDRINFO +static void * +cs_getnameinfo(void *data) +{ + struct _addrinfo_msg *msg = data; + int herr; + struct hostent h; + struct sockaddr_in *sin = (struct sockaddr_in *)msg->addr; + + /* FIXME: error code */ + if (msg->addr->sa_family != AF_INET) { + msg->result = -1; + return NULL; + } + + /* FIXME: honour getnameinfo flags: do we care, not really */ + + while ((msg->result = camel_gethostbyaddr_r((const char *)&sin->sin_addr, sizeof(sin->sin_addr), AF_INET, &h, + msg->hostbufmem, msg->hostbuflen, &herr)) == ERANGE) { + pthread_testcancel (); + msg->hostbuflen *= 2; + msg->hostbufmem = g_realloc(msg->hostbufmem, msg->hostbuflen); + } + + if (msg->cancelled) + goto cancel; + + if (msg->host) { + g_free(msg->host); + if (msg->result == 0 && h.h_name && h.h_name[0]) { + msg->host = g_strdup(h.h_name); + } else { + unsigned char *in = (unsigned char *)&sin->sin_addr; + + /* sin_addr is always network order which is big-endian */ + msg->host = g_strdup_printf("%u.%u.%u.%u", in[0], in[1], in[2], in[3]); + } + } + + /* we never actually use this anyway */ + if (msg->serv) + sprintf(msg->serv, "%d", sin->sin_port); + + e_msgport_reply((EMsg *)msg); + return NULL; +cancel: + cs_freeinfo(msg); + return NULL; +} +#else +static void * +cs_getnameinfo(void *data) +{ + struct _addrinfo_msg *msg = data; + + /* there doens't appear to be a return code which says host or serv buffers are too short, lengthen them */ + msg->result = getnameinfo(msg->addr, msg->addrlen, msg->host, msg->hostlen, msg->serv, msg->servlen, msg->flags); + + if (msg->cancelled) + cs_freeinfo(msg); + else + e_msgport_reply((EMsg *)msg); + + return NULL; +} +#endif + +int +camel_getnameinfo(const struct sockaddr *sa, socklen_t salen, char **host, char **serv, int flags, CamelException *ex) +{ + struct _addrinfo_msg *msg; + int result; + + if (camel_operation_cancel_check(NULL)) { + camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Cancelled")); + return -1; + } + + camel_operation_start_transient(NULL, _("Resolving address")); + + msg = g_malloc0(sizeof(*msg)); + msg->addr = sa; + msg->addrlen = salen; + if (host) { + msg->hostlen = NI_MAXHOST; + msg->host = g_malloc(msg->hostlen); + msg->host[0] = 0; + } + if (serv) { + msg->servlen = NI_MAXSERV; + msg->serv = g_malloc(msg->servlen); + msg->serv[0] = 0; + } + msg->flags = flags; +#ifdef NEED_ADDRINFO + msg->hostbuflen = 1024; + msg->hostbufmem = g_malloc(msg->hostbuflen); +#endif + cs_waitinfo(cs_getnameinfo, msg, _("Name lookup failed"), ex); + + if ((result = msg->result) != 0) + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Name lookup failed: %s"), + gai_strerror (result)); + + if (host) + *host = g_strdup(msg->host); + if (serv) + *serv = g_strdup(msg->serv); + + g_free(msg->host); + g_free(msg->serv); + g_free(msg); + + camel_operation_end(NULL); + + return result; +} -- cgit v1.2.3