From owner-freebsd-java@FreeBSD.ORG Thu Jun 2 21:44:44 2005 Return-Path: X-Original-To: freebsd-java@freebsd.org Delivered-To: freebsd-java@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 16ACF16A41C for ; Thu, 2 Jun 2005 21:44:44 +0000 (GMT) (envelope-from landonf@bikemonkey.org) Received: from goldfish.bikemonkey.org (goldfish.bikemonkey.org [64.81.64.61]) by mx1.FreeBSD.org (Postfix) with ESMTP id 755EC43D4C for ; Thu, 2 Jun 2005 21:44:43 +0000 (GMT) (envelope-from landonf@bikemonkey.org) Received: from [192.168.50.11] (nat.earth.threerings.net [64.81.51.45]) (using TLSv1 with cipher RC4-SHA (128/128 bits)) (No client certificate requested) by goldfish.bikemonkey.org (Postfix) with ESMTP id 74A8CAF600F for ; Thu, 2 Jun 2005 14:44:42 -0700 (PDT) Mime-Version: 1.0 (Apple Message framework v728) In-Reply-To: <94DBA6D4-8B4E-41ED-9297-7EEA7EE9571D@threerings.net> References: <94DBA6D4-8B4E-41ED-9297-7EEA7EE9571D@threerings.net> Content-Type: multipart/signed; protocol="application/pgp-signature"; micalg=pgp-sha1; boundary="Apple-Mail-11--144960713" Message-Id: <632DBC18-546A-477B-8F58-6BCD295943CE@bikemonkey.org> From: Landon Fuller Date: Thu, 2 Jun 2005 14:44:36 -0700 To: freebsd-java@freebsd.org Content-Transfer-Encoding: 7bit X-Pgp-Agent: GPGMail 1.1 (Tiger) X-Mailer: Apple Mail (2.728) Subject: Re: jdk1.4.2 & Inet4Address resolver thread-safety X-BeenThere: freebsd-java@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Porting Java to FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 02 Jun 2005 21:44:44 -0000 --Apple-Mail-11--144960713 Content-Type: multipart/mixed; boundary=Apple-Mail-10--144960778 --Apple-Mail-10--144960778 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=US-ASCII; delsp=yes; format=flowed On May 28, 2005, at 9:13 PM, Landon Fuller wrote: > The first involves a lack of thread-safety in j2se/src/solaris/ > native/java/net/Inet4AddressImpl.c due to the use of gethostbyname > ()/gethostbyaddr() on BSD systems: > > An unexpected exception has been detected in native code > outside the VM. > Unexpected Signal : 11 occurred at PC=0x481328DB > Function=strcasecmp+0x33 > Library=/lib/libc.so.5 > > Current Java thread: > at java.net.Inet4AddressImpl.getLocalHostName(Native > Method) > at java.net.InetAddress.getLocalHost(InetAddress.java: > 1178) > at javax.mail.internet.InternetAddress.getLocalAddress > (InternetAddress.java:477) In the Sun implementation of j2se/src/solaris/native/java/net/ Inet4AddressImpl.c, the re-entrant functions gethostbyname_r() and gethostbyaddr_r() are used for lookups. Given FreeBSD's lack of the _r resolver functions, the FreeBSD port uses the non-reentrant, non-thread safe gethostbyname() and gethostbyaddr(). getaddrinfo()/gethostinfo() are intended to serve as thread-safe replacements for gethostbyname() and gethostbyaddr(). Over the past few years, FreeBSD's resolver and getaddrinfo()/gethostinfo() implementations' thread-safety has been gradually improved. As of 5.4-RELEASE, getaddrinfo() and gethostinfo() are fully thread- safe in so far as you do not call the gethostby*/getservby* functions. As April 15th, -CURRENT's getaddrinfo() is fully thread-safe. As of May 16th, the requisite changes have been MFC'd to RELENG_5. Earlier versions may also be mostly thread-safe, but I have not dug through the repository sufficiently to say for sure. Given that getaddrinfo() is thread-safe (with restrictions) as of FreeBSD 5.4, and fully thread-safe/reentrant in CURRENT and RELENG_5, I modified Inet4AddressImpl.c to use getaddrinfo()/gethostinfo() with ai_family set to AF_INET if USE_GETADDRINFO is defined, and added a #define for USE_GETADDRINFO to port_after.h. A patch against Java 1.4.2p7_1 is attached to this message. I'm very new to the code base, so please review the patch and let me know what you think. I'm happy to make any changes necessary. Thanks, Landon Fuller --Apple-Mail-10--144960778 Content-Transfer-Encoding: 7bit Content-Type: application/octet-stream; x-unix-mode=0644; name="gethostby-3.diff" Content-Disposition: attachment; filename=gethostby-3.diff Only in gethostby: cscope.out Only in gethostby/j2se/make/common: .Defs-bsd.gmk.swp Only in gethostby/j2se/src/solaris/native/java/net: .Inet4AddressImpl.c.orig.swp diff -ru bsdjdk/j2se/src/solaris/native/java/net/Inet4AddressImpl.c gethostby/j2se/src/solaris/native/java/net/Inet4AddressImpl.c --- bsdjdk/j2se/src/solaris/native/java/net/Inet4AddressImpl.c Fri May 13 18:04:33 2005 +++ gethostby/j2se/src/solaris/native/java/net/Inet4AddressImpl.c Sat May 28 20:34:54 2005 @@ -11,43 +11,27 @@ #include #include #include +#include #include #include -#if defined(_BSD_SOURCE) -#include -#include "port_before.h" -#include "port_after.h" -#endif #include "jvm.h" #include "jni_util.h" #include "net_util.h" +#include "port_before.h" +#include "port_after.h" #include "java_net_Inet4AddressImpl.h" +#ifndef USE_GETADDRINFO + /* the initial size of our hostent buffers */ #define HENT_BUF_SIZE 1024 #define BIG_HENT_BUF_SIZE 10240 /* a jumbo-sized one */ -#if !defined(__GLIBC__) && !defined(_BSD_SOURCE) +#ifndef __GLIBC__ /* gethostname() is in libc.so but I can't find a header file for it */ extern int gethostname(char *buf, int buf_len); -#endif /* !__GLIBC__ && !_BSD_SOURCE */ - -/* forward declarations of reentrant bind functions. originally added - by billh in jdk 1.3.1/FreeBSD and later cleaned up by me. --phantom */ - -#ifdef HOST_R_RETURN - -HOST_R_RETURN -JDK_gethostbyname_r(const char *name, struct hostent *hptr, - HOST_R_ARGS); - -HOST_R_RETURN -JDK_gethostbyaddr_r(const char *addr, int len, int type, - struct hostent *hptr, - HOST_R_ARGS); - #endif /************************************************************************ @@ -68,11 +52,8 @@ /* Something went wrong, maybe networking is not setup? */ strcpy(hostname, "localhost"); } else { - /* - * XXX: The following comments refer to convention not enforcement. - */ -#if defined(__linux__) || defined(_BSD_SOURCE) - /* On Linux and *BSD gethostname() says "host.domain.sun.com". On +#ifdef __linux__ + /* On Linux gethostname() says "host.domain.sun.com". On * Solaris gethostname() says "host", so extra work is needed. */ #else @@ -82,7 +63,7 @@ * if NIS comes first (it still gets only a partial name). * We use thread-safe system calls. */ -#endif /* __linux__ || _BSD_SOURCE */ +#endif /* __linux__ */ struct hostent res, res2, *hp; char buf[HENT_BUF_SIZE]; char buf2[HENT_BUF_SIZE]; @@ -91,14 +72,14 @@ #ifdef __GLIBC__ gethostbyname_r(hostname, &res, buf, sizeof(buf), &hp, &h_error); #else - hp = JDK_gethostbyname_r(hostname, &res, buf, sizeof(buf), &h_error); + hp = gethostbyname_r(hostname, &res, buf, sizeof(buf), &h_error); #endif if (hp) { #ifdef __GLIBC__ gethostbyaddr_r(hp->h_addr, hp->h_length, AF_INET, &res2, buf2, sizeof(buf2), &hp, &h_error); #else - hp = JDK_gethostbyaddr_r(hp->h_addr, hp->h_length, AF_INET, + hp = gethostbyaddr_r(hp->h_addr, hp->h_length, AF_INET, &res2, buf2, sizeof(buf2), &h_error); #endif if (hp) { @@ -164,7 +145,7 @@ #ifdef __GLIBC__ gethostbyname_r(hostname, &res, buf, sizeof(buf), &hp, &h_error); #else - hp = JDK_gethostbyname_r(hostname, &res, buf, sizeof(buf), &h_error); + hp = gethostbyname_r(hostname, &res, buf, sizeof(buf), &h_error); #endif /* With the re-entrant system calls, it's possible that the buffer @@ -178,7 +159,7 @@ gethostbyname_r(hostname, &res, tmp, BIG_HENT_BUF_SIZE, &hp, &h_error); #else - hp = JDK_gethostbyname_r(hostname, &res, tmp, BIG_HENT_BUF_SIZE, + hp = gethostbyname_r(hostname, &res, tmp, BIG_HENT_BUF_SIZE, &h_error); #endif } @@ -267,7 +248,7 @@ gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET, &hent, buf, sizeof(buf), &hp, &h_error); #else - hp = JDK_gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET, &hent, + hp = gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET, &hent, buf, sizeof(buf), &h_error); #endif /* With the re-entrant system calls, it's possible that the buffer @@ -281,7 +262,7 @@ gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET, &hent, tmp, BIG_HENT_BUF_SIZE, &hp, &h_error); #else - hp = JDK_gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET, + hp = gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET, &hent, tmp, BIG_HENT_BUF_SIZE, &h_error); #endif } else { @@ -300,213 +281,263 @@ } -#if defined(_BSD_SOURCE) - -#if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: gethostent_r.c,v 8.5 2000/07/11 05:46:35 vixie Exp $"; -#endif /* LIBC_SCCS and not lint */ - -#include -#if !defined(_REENTRANT) || !defined(DO_PTHREADS) - static int gethostent_r_not_required = 0; -#else - -#ifdef HOST_R_RETURN - -static HOST_R_RETURN -copy_hostent(struct hostent *, struct hostent *, HOST_R_COPY_ARGS); - -HOST_R_RETURN -JDK_gethostbyname_r(const char *name, struct hostent *hptr, HOST_R_ARGS) { - struct hostent *he = gethostbyname(name); +#else /* USE_GETADDRINFO */ - HOST_R_ERRNO; - - if (he == NULL) - return (HOST_R_BAD); +/************************************************************************ + * Inet4AddressImpl + */ - return (copy_hostent(he, hptr, HOST_R_COPY)); -} +/* + * Class: java_net_Inet4AddressImpl + * Method: getLocalHostName + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL +Java_java_net_Inet4AddressImpl_getLocalHostName(JNIEnv *env, jobject this) { + char hostname[NI_MAXHOST+1]; -HOST_R_RETURN -JDK_gethostbyaddr_r(const char *addr, int len, int type, - struct hostent *hptr, HOST_R_ARGS) { - struct hostent *he = gethostbyaddr(addr, len, type); + hostname[0] = '\0'; + if (JVM_GetHostName(hostname, NI_MAXHOST)) { + /* Something went wrong, maybe networking is not setup? */ + strcpy(hostname, "localhost"); + } else { + /* + * XXX: The following comments refer to convention not enforcement. + */ +#if defined(__linux__) || defined(_BSD_SOURCE) + /* On Linux and *BSD gethostname() says "host.domain.sun.com". On + * Solaris gethostname() says "host", so extra work is needed. + */ +#else + /* Solaris doesn't want to give us a fully qualified domain name. + * We do a reverse lookup to try and get one. This works + * if DNS occurs before NIS in /etc/resolv.conf, but fails + * if NIS comes first (it still gets only a partial name). + * We use thread-safe system calls. + */ +#endif /* __linux__ || _BSD_SOURCE */ + struct addrinfo hints, *res; + int error; - HOST_R_ERRNO; + bzero(&hints, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = AF_UNSPEC; + + error = getaddrinfo(hostname, "domain", &hints, &res); + + if (error == 0) { + /* host is known to name service */ + error = getnameinfo(res->ai_addr, + res->ai_addrlen, + hostname, + NI_MAXHOST, + NULL, + 0, + NI_NAMEREQD); - if (he == NULL) - return (HOST_R_BAD); + /* if getnameinfo fails hostname is still the value + from gethostname */ - return (copy_hostent(he, hptr, HOST_R_COPY)); + freeaddrinfo(res); + } + return (*env)->NewStringUTF(env, hostname); + } } /* - * These assume a single context is in operation per thread. - * If this is not the case we will need to call irs directly - * rather than through the base functions. + * Find an internet address for a given hostname. Note that this + * code only works for addresses of type INET. The translation + * of %d.%d.%d.%d to an address (int) occurs in java now, so the + * String "host" shouldn't *ever* be a %d.%d.%d.%d string + * + * Class: java_net_Inet4AddressImpl + * Method: lookupAllHostAddr + * Signature: (Ljava/lang/String;)[[B */ -#if !defined(__NetBSD__) /* gethostent is depricated */ -HOST_R_RETURN -gethostent_r(struct hostent *hptr, HOST_R_ARGS) { - struct hostent *he = gethostent(); - - HOST_R_ERRNO; - - if (he == NULL) - return (HOST_R_BAD); - - return (copy_hostent(he, hptr, HOST_R_COPY)); -} -#endif +JNIEXPORT jobjectArray JNICALL +Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this, + jstring host) { + const char *hostname; + jobjectArray ret = 0; + int retLen = 0; + jclass byteArrayCls; -HOST_R_SET_RETURN -#ifdef HOST_R_ENT_ARGS -sethostent_r(int stay_open, HOST_R_ENT_ARGS) -#else -sethostent_r(int stay_open) -#endif -{ - sethostent(stay_open); -#ifdef HOST_R_SET_RESULT - return (HOST_R_SET_RESULT); -#endif -} + int error=0; + struct addrinfo hints, *res, *resNew = NULL; -HOST_R_END_RETURN -#ifdef HOST_R_ENT_ARGS -endhostent_r(HOST_R_ENT_ARGS) -#else -endhostent_r() -#endif -{ - endhostent(); - HOST_R_END_RESULT(HOST_R_OK); -} + if (IS_NULL(host)) { + JNU_ThrowNullPointerException(env, "host is null"); + return 0; + } + hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE); + CHECK_NULL_RETURN(hostname, NULL); -/* Private */ + bzero(&hints, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = AF_INET; -#ifndef HOSTENT_DATA -static HOST_R_RETURN -copy_hostent(struct hostent *he, struct hostent *hptr, HOST_R_COPY_ARGS) { - char *cp; - char **ptr; - int i, n; - int nptr, len; - - /* Find out the amount of space required to store the answer. */ - nptr = 2; /* NULL ptrs */ - len = (char *)ALIGN(buf) - buf; - for (i = 0; he->h_addr_list[i]; i++, nptr++) { - len += he->h_length; - } - for (i = 0; he->h_aliases[i]; i++, nptr++) { - len += strlen(he->h_aliases[i]) + 1; - } - len += strlen(he->h_name) + 1; - len += nptr * sizeof(char*); + /* + * Workaround for Solaris bug 4160367 - if a hostname contains a + * white space then 0.0.0.0 is returned + */ + if (isspace(hostname[0])) { + JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", + (char *)hostname); + JNU_ReleaseStringPlatformChars(env, host, hostname); + return NULL; + } - if (len > buflen) { - errno = ERANGE; - return (HOST_R_BAD); - } - - /* copy address size and type */ - hptr->h_addrtype = he->h_addrtype; - n = hptr->h_length = he->h_length; - - ptr = (char **)ALIGN(buf); - cp = (char *)ALIGN(buf) + nptr * sizeof(char *); - - /* copy address list */ - hptr->h_addr_list = ptr; - for (i = 0; he->h_addr_list[i]; i++ , ptr++) { - memcpy(cp, he->h_addr_list[i], n); - hptr->h_addr_list[i] = cp; - cp += n; - } - hptr->h_addr_list[i] = NULL; - ptr++; - - /* copy official name */ - n = strlen(he->h_name) + 1; - strcpy(cp, he->h_name); - hptr->h_name = cp; - cp += n; - - /* copy aliases */ - hptr->h_aliases = ptr; - for (i = 0 ; he->h_aliases[i]; i++) { - n = strlen(he->h_aliases[i]) + 1; - strcpy(cp, he->h_aliases[i]); - hptr->h_aliases[i] = cp; - cp += n; - } - hptr->h_aliases[i] = NULL; - - return (HOST_R_OK); -} -#else /* !HOSTENT_DATA */ -static int -copy_hostent(struct hostent *he, struct hostent *hptr, HOST_R_COPY_ARGS) { - char *cp, *eob; - int i, n; - - /* copy address size and type */ - hptr->h_addrtype = he->h_addrtype; - n = hptr->h_length = he->h_length; - - /* copy up to first 35 addresses */ - i = 0; - cp = hdptr->hostaddr; - eob = hdptr->hostaddr + sizeof(hdptr->hostaddr); - hptr->h_addr_list = hdptr->h_addr_ptrs; - while (he->h_addr_list[i] && i < (_MAXADDRS)) { - if (n < (eob - cp)) { - memcpy(cp, he->h_addr_list[i], n); - hptr->h_addr_list[i] = cp; - cp += n; + error = getaddrinfo(hostname, "domain", &hints, &res); + + if (error) { + /* report error */ + JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", + (char *)hostname); + JNU_ReleaseStringPlatformChars(env, host, hostname); + return NULL; + } else { + int i = 0; + struct addrinfo *itr, *last, *iterator = res; + while (iterator != NULL) { + int skip = 0; + itr = resNew; + + while (itr != NULL) { + struct sockaddr_in *addr1, *addr2; + + addr1 = (struct sockaddr_in *)iterator->ai_addr; + addr2 = (struct sockaddr_in *)itr->ai_addr; + if (addr1->sin_addr.s_addr == + addr2->sin_addr.s_addr) { + skip = 1; + break; + } + + itr = itr->ai_next; + } + + if (!skip) { + struct addrinfo *next + = (struct addrinfo*) malloc(sizeof(struct addrinfo)); + if (!next) { + JNU_ThrowOutOfMemoryError(env, "heap allocation failed"); + ret = NULL; + goto cleanupAndReturn; + } + memcpy(next, iterator, sizeof(struct addrinfo)); + next->ai_next = NULL; + if (resNew == NULL) { + resNew = next; } else { - break; + last->ai_next = next; } + last = next; i++; + } + iterator = iterator->ai_next; } - hptr->h_addr_list[i] = NULL; - /* copy official name */ - cp = hdptr->hostbuf; - eob = hdptr->hostbuf + sizeof(hdptr->hostbuf); - if ((n = strlen(he->h_name) + 1) < (eob - cp)) { - strcpy(cp, he->h_name); - hptr->h_name = cp; - cp += n; - } else { - return (-1); + retLen = i; + iterator = resNew; + i = 0; + byteArrayCls = (*env)->FindClass(env, "[B"); + if (byteArrayCls == NULL) { + /* pending exception */ + goto cleanupAndReturn; + } + ret = (*env)->NewObjectArray(env, retLen, byteArrayCls, NULL); + + if (IS_NULL(ret)) { + /* we may have memory to free at the end of this */ + goto cleanupAndReturn; } - /* copy aliases */ - i = 0; - hptr->h_aliases = hdptr->host_aliases; - while (he->h_aliases[i] && i < (_MAXALIASES-1)) { - if ((n = strlen(he->h_aliases[i]) + 1) < (eob - cp)) { - strcpy(cp, he->h_aliases[i]); - hptr->h_aliases[i] = cp; - cp += n; - } else { - break; - } - i++; + while (iterator != NULL) { + /* We need 4 bytes to store ipv4 address; */ + int len = 4; + jbyteArray barray = (*env)->NewByteArray(env, len); + + if (IS_NULL(barray)) { + /* we may have memory to free at the end of this */ + ret = NULL; + goto cleanupAndReturn; + } + (*env)->SetByteArrayRegion(env, barray, 0, len, + (jbyte *) &((struct sockaddr_in*)(iterator->ai_addr))->sin_addr); + (*env)->SetObjectArrayElement(env, ret, retLen - i -1, barray); + i++; + iterator = iterator->ai_next; } - hptr->h_aliases[i] = NULL; + } + +cleanupAndReturn: + { + struct addrinfo *iterator, *tmp; + iterator = resNew; + while (iterator != NULL) { + tmp = iterator; + iterator = iterator->ai_next; + free(tmp); + } + JNU_ReleaseStringPlatformChars(env, host, hostname); + } + + freeaddrinfo(res); + + return ret; - return (HOST_R_OK); } -#endif /* !HOSTENT_DATA */ -#else /* HOST_R_RETURN */ - static int gethostent_r_unknown_systemm = 0; -#endif /* HOST_R_RETURN */ -#endif /* !defined(_REENTRANT) || !defined(DO_PTHREADS) */ -#endif +/* + * Class: java_net_Inet4AddressImpl + * Method: getHostByAddr + * Signature: (I)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL +Java_java_net_Inet4AddressImpl_getHostByAddr(JNIEnv *env, jobject this, + jbyteArray addrArray) { + jstring ret = NULL; + + char host[NI_MAXHOST+1]; + jfieldID fid; + int error = 0; + jint family; + struct sockaddr *him ; + int len = 0; + jbyte caddr[4]; + jint addr; + + struct sockaddr_in him4; + struct sockaddr *sa; + + /* + * For IPv4 addresses construct a sockaddr_in structure. + */ + (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr); + addr = ((caddr[0]<<24) & 0xff000000); + addr |= ((caddr[1] <<16) & 0xff0000); + addr |= ((caddr[2] <<8) & 0xff00); + addr |= (caddr[3] & 0xff); + memset((char *) &him4, 0, sizeof(him4)); + him4.sin_addr.s_addr = (uint32_t) htonl(addr); + him4.sin_family = AF_INET; + sa = (struct sockaddr *) &him4; + len = sizeof(him4); + + error = getnameinfo(sa, len, host, NI_MAXHOST, NULL, 0, + NI_NAMEREQD); + + if (!error) { + ret = (*env)->NewStringUTF(env, host); + } + + if (ret == NULL) { + JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", NULL); + } + + return ret; + +} +#endif /* USE_GETADDRINFO */ Only in gethostby/j2se/src/solaris/native/java/net: Inet4AddressImpl.c.origdiff Only in gethostby/j2se/src/solaris/native/java/net: foo.c diff -ru bsdjdk/j2se/src/solaris/native/java/net/port_after.h gethostby/j2se/src/solaris/native/java/net/port_after.h --- bsdjdk/j2se/src/solaris/native/java/net/port_after.h Fri May 13 18:04:33 2005 +++ gethostby/j2se/src/solaris/native/java/net/port_after.h Sat May 28 20:05:41 2005 @@ -5,6 +5,7 @@ #define POSIX_SIGNALS #define USE_UTIME #define USE_WAITPID +#define USE_GETADDRINFO #define HAVE_GETRUSAGE #define HAVE_FCHMOD #define NEED_PSELECT --Apple-Mail-10--144960778-- --Apple-Mail-11--144960713 content-type: application/pgp-signature; x-mac-type=70674453; name=PGP.sig content-description: This is a digitally signed message part content-disposition: inline; filename=PGP.sig content-transfer-encoding: 7bit -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.1 (Darwin) iD8DBQFCn33IlplZCE/15mMRAugdAJ4/+5kOBZTQXeMlCAaubopDrtNSyACfUQs8 A7+nMXfhxGKUgpFVLvaWy5E= =lWkS -----END PGP SIGNATURE----- --Apple-Mail-11--144960713--