* #27232: jni: added pjproject checkout as regular git content

We will remove it once the next release of pjsip (with Android support)
comes out and is merged into SFLphone.
diff --git a/jni/pjproject-android/.svn/pristine/91/911f47c20ab55f9b44ad90cb0ea9a9561ce5150c.svn-base b/jni/pjproject-android/.svn/pristine/91/911f47c20ab55f9b44ad90cb0ea9a9561ce5150c.svn-base
new file mode 100644
index 0000000..af28f40
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/91/911f47c20ab55f9b44ad90cb0ea9a9561ce5150c.svn-base
@@ -0,0 +1,127 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 
+ */
+#include <pjmedia/resample.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+
+#if PJMEDIA_RESAMPLE_IMP==PJMEDIA_RESAMPLE_SPEEX
+
+#include <speex/speex_resampler.h>
+
+#define THIS_FILE   "resample_speex.c"
+
+
+struct pjmedia_resample
+{
+    SpeexResamplerState *state;
+    unsigned		 in_samples_per_frame;
+    unsigned		 out_samples_per_frame;
+};
+
+
+PJ_DEF(pj_status_t) pjmedia_resample_create( pj_pool_t *pool,
+					     pj_bool_t high_quality,
+					     pj_bool_t large_filter,
+					     unsigned channel_count,
+					     unsigned rate_in,
+					     unsigned rate_out,
+					     unsigned samples_per_frame,
+					     pjmedia_resample **p_resample)
+{
+    pjmedia_resample *resample;
+    int quality;
+    int err;
+
+    PJ_ASSERT_RETURN(pool && p_resample && rate_in &&
+		     rate_out && samples_per_frame, PJ_EINVAL);
+
+    resample = PJ_POOL_ZALLOC_T(pool, pjmedia_resample);
+    PJ_ASSERT_RETURN(resample, PJ_ENOMEM);
+
+    if (high_quality) {
+	if (large_filter)
+	    quality = 10;
+	else
+	    quality = 7;
+    } else {
+	quality = 3;
+    }
+
+    resample->in_samples_per_frame = samples_per_frame;
+    resample->out_samples_per_frame = rate_out / (rate_in / samples_per_frame);
+    resample->state = speex_resampler_init(channel_count,  rate_in, rate_out, 
+                                           quality, &err);
+    if (resample->state == NULL || err != RESAMPLER_ERR_SUCCESS)
+	return PJ_ENOMEM;
+
+
+    *p_resample = resample;
+
+    PJ_LOG(5,(THIS_FILE, 
+	      "resample created: quality=%d, ch=%d, in/out rate=%d/%d", 
+	      quality, channel_count, rate_in, rate_out));
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(void) pjmedia_resample_run( pjmedia_resample *resample,
+				   const pj_int16_t *input,
+				   pj_int16_t *output )
+{
+    spx_uint32_t in_length, out_length;
+
+    PJ_ASSERT_ON_FAIL(resample, return);
+
+    in_length = resample->in_samples_per_frame;
+    out_length = resample->out_samples_per_frame;
+
+    speex_resampler_process_interleaved_int(resample->state,
+					    (const __int16 *)input, &in_length,
+					    (__int16 *)output, &out_length);
+
+    pj_assert(in_length == resample->in_samples_per_frame);
+    pj_assert(out_length == resample->out_samples_per_frame);
+}
+
+
+PJ_DEF(unsigned) pjmedia_resample_get_input_size(pjmedia_resample *resample)
+{
+    PJ_ASSERT_RETURN(resample != NULL, 0);
+    return resample->in_samples_per_frame;
+}
+
+
+PJ_DEF(void) pjmedia_resample_destroy(pjmedia_resample *resample)
+{
+    PJ_ASSERT_ON_FAIL(resample, return);
+    if (resample->state) {
+	speex_resampler_destroy(resample->state);
+	resample->state = NULL;
+    }
+}
+
+#else /* PJMEDIA_RESAMPLE_IMP==PJMEDIA_RESAMPLE_SPEEX */
+
+int pjmedia_resample_speex_excluded;
+
+#endif	/* PJMEDIA_RESAMPLE_IMP==PJMEDIA_RESAMPLE_SPEEX */
+
diff --git a/jni/pjproject-android/.svn/pristine/91/91414ff3f05c02d69c501218967d353e07124df3.svn-base b/jni/pjproject-android/.svn/pristine/91/91414ff3f05c02d69c501218967d353e07124df3.svn-base
new file mode 100644
index 0000000..5a12cf7
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/91/91414ff3f05c02d69c501218967d353e07124df3.svn-base
@@ -0,0 +1,291 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 
+ */
+#ifndef __PJSIP_SIP_RESOLVE_H__
+#define __PJSIP_SIP_RESOLVE_H__
+
+/**
+ * @file sip_resolve.h
+ * @brief 
+ * This module contains the mechanism to resolve server address as specified by
+ * RFC 3263 - Locating SIP Servers
+ */
+
+#include <pjsip/sip_types.h>
+#include <pjlib-util/resolver.h>
+#include <pj/sock.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_RESOLVE SIP SRV Server Resolution (RFC 3263 - Locating SIP Servers)
+ * @ingroup PJSIP_TRANSPORT
+ * @brief Framework to resolve SIP servers based on RFC 3263.
+ * @{
+ * \section PJSIP_RESOLVE_FEATURES Features
+ *
+ * This is the SIP server resolution framework, which is modelled after 
+ * RFC 3263 - Locating SIP Servers document. The SIP server resolution
+ * framework is asynchronous; callback will be called once the server 
+ * address has been resolved (successfully or with errors).
+ *
+ * \subsection PJSIP_RESOLVE_CONFORMANT Conformance to RFC 3263
+ *
+ * The SIP server resolution framework is modelled after RFC 3263 (Locating 
+ * SIP Servers) document, and it provides a single function (#pjsip_resolve())
+ * to resolve a domain into actual IP addresses of the servers, by querying 
+ * DNS SRV record and DNS A record where necessary.
+ *
+ * The #pjsip_resolve() function performs the server resolution according
+ * to RFC 3263 with some additional fallback mechanisms, as follows:
+ *  - if the target name is an IP address, the callback will be called
+ *    immediately with the IP address. If port number was specified, this
+ *    port number will be used, otherwise the default port number for the
+ *    transport will be used (5060 for TCP/UDP, 5061 for TLS) if the transport
+ *    is specified. If the transport is not specified, UDP with port number
+ *    5060 will be used.
+ *  - if target name is not an IP address but it contains port number,
+ *    then the target name is resolved with DNS A (or AAAA, when IPv6 is
+ *    supported in the future) query, and the port is taken from the
+ *    port number argument. The callback will be called once the DNS A
+ *    resolution completes. If the DNS A resolution returns multiple IP
+ *    addresses, these IP addresses will be returned to the caller.
+ *  - if target name is not an IP address and port number is not specified,
+ *    DNS SRV resolution will be performed for the specified name and
+ *    transport type (or UDP when transport is not specified), 
+ *    then followed by DNS A (or AAAA, when IPv6 is supported)
+ *    resolution for each target in the SRV record. If DNS SRV
+ *    resolution returns error, DNS A (or AAAA) resolution will be
+ *    performed for the original target (it is assumed that the target domain
+ *    does not support SRV records). Upon successful completion, 
+ *    application callback will be called with each IP address of the
+ *    target selected based on the load-balancing and fail-over criteria
+ *    below.
+ *
+ * The above server resolution procedure differs from RFC 3263 in these
+ * regards:
+ *  - currently #pjsip_resolve() doesn't support DNS NAPTR record.
+ *  - if transport is not specified, it is assumed to be UDP (the proper
+ *    behavior is to query the NAPTR record, but we don't support this
+ *    yet).
+ *
+ *
+ * \subsection PJSIP_SIP_RESOLVE_FAILOVER_LOADBALANCE Load-Balancing and Fail-Over
+ *
+ * When multiple targets are returned in the DNS SRV response, server entries
+ * are selected based on the following rule (which is described in RFC 2782):
+ *  - targets will be sorted based on the priority first.
+ *  - for targets with the same priority, #pjsip_resolve() will select
+ *    only one target according to its weight. To select this one target,
+ *    the function associates running-sum for all targets, and generates 
+ *    a random number between zero and the total running-sum (inclusive).
+ *    The target selected is the first target with running-sum greater than
+ *    or equal to this random number.
+ *
+ * The above procedure will select one target for each priority, allowing
+ * application to fail-over to the next target when the previous target fails.
+ * These targets are returned in the #pjsip_server_addresses structure 
+ * argument of the callback. 
+ *
+ * \subsection PJSIP_SIP_RESOLVE_SIP_FEATURES SIP SRV Resolver Features
+ *
+ * Some features of the SIP resolver:
+ *  - DNS SRV entries are returned on sorted order based on priority 
+ *    to allow failover to the next appropriate server.
+ *  - The procedure in RFC 2782 is used to select server with the same
+ *    priority to load-balance the servers load.
+ *  - A single function (#pjsip_resolve()) performs all server resolution
+ *    works, from resolving the SRV records to getting the actual IP addresses
+ *    of the servers with DNS A (or AAAA) resolution.
+ *  - When multiple DNS SRV records are returned, parallel DNS A (or AAAA)
+ *    queries will be issued simultaneously.
+ *  - The PJLIB-UTIL DNS resolver provides additional functionality such as
+ *    response caching, query aggregation, parallel nameservers, fallback
+ *    nameserver, etc., which will be described below.
+ * 
+ *
+ * \subsection PJSIP_RESOLVE_DNS_FEATURES DNS Resolver Features
+ *
+ * The PJSIP server resolution framework uses PJLIB-UTIL DNS resolver engine
+ * for performing the asynchronous DNS request. The PJLIB-UTIL DNS resolver
+ * has some useful features, such as:
+ *  - queries are asynchronous with configurable timeout,
+ *  - query aggregation to combine multiple pending queries to the same
+ *    DNS target into a single DNS request (to save message round-trip and
+ *    processing),
+ *  - response caching with TTL negotiated between the minimum TTL found in
+ *    the response and the maximum TTL allowed in the configuration,
+ *  - multiple nameservers, with active nameserver is selected from nameserver
+ *    which provides the best response time,
+ *  - fallback nameserver, with periodic detection of which name servers are
+ *    active or down.
+ *  - etc.
+ *
+ * Please consult PJLIB-UTIL DNS resolver documentation for more details.
+ *
+ *
+ * \section PJSIP_RESOLVE_USING Using the Resolver
+ *
+ * To maintain backward compatibility, the resolver MUST be enabled manually.
+ * With the default settings, the resolver WILL NOT perform DNS SRV resolution,
+ * as it will just resolve the name with standard pj_gethostbyname() function.
+ *
+ * Application can enable the SRV resolver by creating the PJLIB-UTIL DNS 
+ * resolver with #pjsip_endpt_create_resolver(), configure the
+ * nameservers of the PJLIB-UTIL DNS resolver object by calling
+ * pj_dns_resolver_set_ns() function, and pass the DNS resolver object to 
+ * #pjsip_resolver_set_resolver() function.
+ *
+ * Once the resolver is set, it will be used automatically by PJSIP everytime
+ * PJSIP needs to send SIP request/response messages.
+ *
+ *
+ * \section PJSIP_RESOLVE_REFERENCE Reference
+ *
+ * Reference:
+ *  - RFC 2782: A DNS RR for specifying the location of services (DNS SRV)
+ *  - RFC 3263: Locating SIP Servers
+ */
+
+/**
+ * The server addresses returned by the resolver.
+ */
+typedef struct pjsip_server_addresses
+{
+    /** Number of address records. */
+    unsigned	count;
+
+    /** Address records. */
+    struct
+    {
+	/** Preferable transport to be used to contact this address. */
+	pjsip_transport_type_e	type;
+
+	/** Server priority (the lower the higher the priority). */
+	unsigned		priority;
+
+	/** Server weight (the higher the more load it can handle). */
+	unsigned		weight;
+
+	/** The server's address. */
+	pj_sockaddr		addr;
+
+	/** Address length. */
+	int			addr_len;
+
+    } entry[PJSIP_MAX_RESOLVED_ADDRESSES];
+
+} pjsip_server_addresses;
+
+
+/**
+ * The type of callback function to be called when resolver finishes the job.
+ *
+ * @param status    The status of the operation, which is zero on success.
+ * @param token	    The token that was associated with the job when application
+ *		    call the resolve function.
+ * @param addr	    The addresses resolved by the operation.
+ */
+typedef void pjsip_resolver_callback(pj_status_t status,
+				     void *token,
+				     const struct pjsip_server_addresses *addr);
+
+/**
+ * Create SIP resolver engine. Note that this function is normally called
+ * internally by pjsip_endpoint instance.
+ *
+ * @param pool	    Pool to allocate memory from.
+ * @param p_res	    Pointer to receive SIP resolver instance.
+ *
+ * @return	    PJ_SUCCESS when resolver can be successfully created.
+ */
+PJ_DECL(pj_status_t) pjsip_resolver_create(pj_pool_t *pool,
+					   pjsip_resolver_t **p_res);
+
+/**
+ * Set the DNS resolver instance of the SIP resolver engine. Before the
+ * DNS resolver is set, the SIP resolver will use standard pj_gethostbyname()
+ * to resolve addresses.
+ *
+ * Note that application normally will use #pjsip_endpt_set_resolver() instead
+ * since it does not normally have access to the SIP resolver instance.
+ *
+ * @param res	    The SIP resolver engine.
+ * @param dns_res   The DNS resolver instance to be used by the SIP resolver.
+ *		    This argument can be NULL to reset the internal DNS
+ *		    instance.
+ *
+ * @return	    PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_resolver_set_resolver(pjsip_resolver_t *res,
+						 pj_dns_resolver *dns_res);
+
+
+/**
+ * Get the DNS resolver instance of the SIP resolver engine. 
+ *
+ * Note that application normally will use #pjsip_endpt_get_resolver() instead
+ * since it does not normally have access to the SIP resolver instance.
+ *
+ * @param res	    The SIP resolver engine.
+ *
+ * @return	    The DNS resolver instance (may be NULL)
+ */
+PJ_DECL(pj_dns_resolver*) pjsip_resolver_get_resolver(pjsip_resolver_t *res);
+
+/**
+ * Destroy resolver engine. Note that this will also destroy the internal
+ * DNS resolver inside the engine. If application doesn't want the internal
+ * DNS resolver to be destroyed, it should set the internal DNS resolver
+ * to NULL before calling this function.
+ *
+ * Note that this function will normally called by the SIP endpoint instance
+ * when the SIP endpoint instance is destroyed.
+ *
+ * @param resolver The resolver.
+ */
+PJ_DECL(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver);
+
+/**
+ * Asynchronously resolve a SIP target host or domain according to rule 
+ * specified in RFC 3263 (Locating SIP Servers). When the resolving operation
+ * has completed, the callback will be called.
+ *
+ * Note that application normally will use #pjsip_endpt_resolve() instead
+ * since it does not normally have access to the SIP resolver instance.
+ *
+ * @param resolver	The resolver engine.
+ * @param pool		The pool to allocate resolver job.
+ * @param target	The target specification to be resolved.
+ * @param token		A user defined token to be passed back to callback function.
+ * @param cb		The callback function.
+ */
+PJ_DECL(void) pjsip_resolve( pjsip_resolver_t *resolver,
+			     pj_pool_t *pool,
+			     const pjsip_host_info *target,
+			     void *token,
+			     pjsip_resolver_callback *cb);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJSIP_SIP_RESOLVE_H__ */
diff --git a/jni/pjproject-android/.svn/pristine/91/916ef30ea6b284975d414c24345cbbae20a0eb4c.svn-base b/jni/pjproject-android/.svn/pristine/91/916ef30ea6b284975d414c24345cbbae20a0eb4c.svn-base
new file mode 100644
index 0000000..ce50902
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/91/916ef30ea6b284975d414c24345cbbae20a0eb4c.svn-base
@@ -0,0 +1,523 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="pjstun_srv_test"

+	ProjectGUID="{CD3783DF-22FA-4BAC-A8A6-62DA0D2CC045}"

+	RootNamespace="pjstun_srv_test"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+		<Platform

+			Name="x64"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Release|Win32"

+			ConfigurationType="1"

+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"

+			UseOfMFC="0"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include"

+				PreprocessorDefinitions="_CONSOLE;"

+				PrecompiledHeaderFile=""

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib "

+				OutputFile="..\bin\pjstun-srv-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Debug|Win32"

+			ConfigurationType="1"

+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"

+			UseOfMFC="0"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include"

+				PreprocessorDefinitions="_CONSOLE;"

+				PrecompiledHeaderFile=""

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="netapi32.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib "

+				OutputFile="..\bin\pjstun-srv-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|x64"

+			ConfigurationType="1"

+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"

+			UseOfMFC="0"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include"

+				PreprocessorDefinitions="_CONSOLE;"

+				PrecompiledHeaderFile=""

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib "

+				OutputFile="..\bin\pjstun-srv-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Debug|x64"

+			ConfigurationType="1"

+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"

+			UseOfMFC="0"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include"

+				PreprocessorDefinitions="_CONSOLE;"

+				PrecompiledHeaderFile=""

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="netapi32.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib "

+				OutputFile="..\bin\pjstun-srv-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"

+			>

+			<File

+				RelativePath="..\src\pjstun-srv-test\bind_usage.c"

+				>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+			</File>

+			<File

+				RelativePath="..\src\pjstun-srv-test\main.c"

+				>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+			</File>

+			<File

+				RelativePath="..\src\pjstun-srv-test\server.c"

+				>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+			</File>

+			<File

+				RelativePath="..\src\pjstun-srv-test\turn_usage.c"

+				>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+			</File>

+			<File

+				RelativePath="..\src\pjstun-srv-test\usage.c"

+				>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						AdditionalIncludeDirectories=""

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl"

+			>

+			<File

+				RelativePath="..\src\pjstun-srv-test\server.h"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"

+			>

+		</Filter>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/jni/pjproject-android/.svn/pristine/91/917b221c024ac960536783db24660a5c95ef3790.svn-base b/jni/pjproject-android/.svn/pristine/91/917b221c024ac960536783db24660a5c95ef3790.svn-base
new file mode 100644
index 0000000..0a14de8
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/91/917b221c024ac960536783db24660a5c95ef3790.svn-base
@@ -0,0 +1,300 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use.  Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+#include <pjmedia/alaw_ulaw.h>
+
+#if !defined(PJMEDIA_HAS_ALAW_ULAW_TABLE) || PJMEDIA_HAS_ALAW_ULAW_TABLE==0
+
+#ifdef _MSC_VER
+#  pragma warning ( disable: 4244 ) /* Conversion from int to char etc */
+#endif
+
+/*
+ * g711.c
+ *
+ * u-law, A-law and linear PCM conversions.
+ */
+#define	SIGN_BIT	(0x80)		/* Sign bit for a A-law byte. */
+#define	QUANT_MASK	(0xf)		/* Quantization field mask. */
+#define	NSEGS		(8)		/* Number of A-law segments. */
+#define	SEG_SHIFT	(4)		/* Left shift for segment number. */
+#define	SEG_MASK	(0x70)		/* Segment field mask. */
+
+static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
+			    0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
+
+/* copy from CCITT G.711 specifications */
+static unsigned char _u2a[128] = {		/* u- to A-law conversions */
+	1,	1,	2,	2,	3,	3,	4,	4,
+	5,	5,	6,	6,	7,	7,	8,	8,
+	9,	10,	11,	12,	13,	14,	15,	16,
+	17,	18,	19,	20,	21,	22,	23,	24,
+	25,	27,	29,	31,	33,	34,	35,	36,
+	37,	38,	39,	40,	41,	42,	43,	44,
+	46,	48,	49,	50,	51,	52,	53,	54,
+	55,	56,	57,	58,	59,	60,	61,	62,
+	64,	65,	66,	67,	68,	69,	70,	71,
+	72,	73,	74,	75,	76,	77,	78,	79,
+	81,	82,	83,	84,	85,	86,	87,	88,
+	89,	90,	91,	92,	93,	94,	95,	96,
+	97,	98,	99,	100,	101,	102,	103,	104,
+	105,	106,	107,	108,	109,	110,	111,	112,
+	113,	114,	115,	116,	117,	118,	119,	120,
+	121,	122,	123,	124,	125,	126,	127,	128};
+
+static unsigned char _a2u[128] = {		/* A- to u-law conversions */
+	1,	3,	5,	7,	9,	11,	13,	15,
+	16,	17,	18,	19,	20,	21,	22,	23,
+	24,	25,	26,	27,	28,	29,	30,	31,
+	32,	32,	33,	33,	34,	34,	35,	35,
+	36,	37,	38,	39,	40,	41,	42,	43,
+	44,	45,	46,	47,	48,	48,	49,	49,
+	50,	51,	52,	53,	54,	55,	56,	57,
+	58,	59,	60,	61,	62,	63,	64,	64,
+	65,	66,	67,	68,	69,	70,	71,	72,
+	73,	74,	75,	76,	77,	78,	79,	79,
+	80,	81,	82,	83,	84,	85,	86,	87,
+	88,	89,	90,	91,	92,	93,	94,	95,
+	96,	97,	98,	99,	100,	101,	102,	103,
+	104,	105,	106,	107,	108,	109,	110,	111,
+	112,	113,	114,	115,	116,	117,	118,	119,
+	120,	121,	122,	123,	124,	125,	126,	127};
+
+static int
+search(
+	int		val,
+	short		*table,
+	int		size)
+{
+	int		i;
+
+	for (i = 0; i < size; i++) {
+		if (val <= *table++)
+			return (i);
+	}
+	return (size);
+}
+
+/*
+ * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
+ *
+ * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
+ *
+ *		Linear Input Code	Compressed Code
+ *	------------------------	---------------
+ *	0000000wxyza			000wxyz
+ *	0000001wxyza			001wxyz
+ *	000001wxyzab			010wxyz
+ *	00001wxyzabc			011wxyz
+ *	0001wxyzabcd			100wxyz
+ *	001wxyzabcde			101wxyz
+ *	01wxyzabcdef			110wxyz
+ *	1wxyzabcdefg			111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+PJ_DEF(pj_uint8_t) pjmedia_linear2alaw(
+	int		pcm_val)	/* 2's complement (16-bit range) */
+{
+	int		mask;
+	int		seg;
+	unsigned char	aval;
+
+	if (pcm_val >= 0) {
+		mask = 0xD5;		/* sign (7th) bit = 1 */
+	} else {
+		mask = 0x55;		/* sign bit = 0 */
+		pcm_val = -pcm_val - 8;
+
+		/* https://trac.pjsip.org/repos/ticket/1301 
+		 * Thank you K Johnson - Zetron - 27 May 2011
+		 */
+		if (pcm_val < 0)
+		    pcm_val = 0;
+	}
+
+	/* Convert the scaled magnitude to segment number. */
+	seg = search(pcm_val, seg_end, 8);
+
+	/* Combine the sign, segment, and quantization bits. */
+
+	if (seg >= 8)		/* out of range, return maximum value. */
+		return (0x7F ^ mask);
+	else {
+		aval = seg << SEG_SHIFT;
+		if (seg < 2)
+			aval |= (pcm_val >> 4) & QUANT_MASK;
+		else
+			aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
+		return (aval ^ mask);
+	}
+}
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit linear PCM
+ *
+ */
+PJ_DEF(int) pjmedia_alaw2linear(
+	unsigned a_val)
+{
+	int		t;
+	int		seg;
+
+	a_val ^= 0x55;
+
+	t = (a_val & QUANT_MASK) << 4;
+	seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+	switch (seg) {
+	case 0:
+		t += 8;
+		break;
+	case 1:
+		t += 0x108;
+		break;
+	default:
+		t += 0x108;
+		t <<= seg - 1;
+	}
+	return ((a_val & SIGN_BIT) ? t : -t);
+}
+
+#define	BIAS		(0x84)		/* Bias for linear code. */
+
+/*
+ * linear2ulaw() - Convert a linear PCM value to u-law
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ *	Biased Linear Input Code	Compressed Code
+ *	------------------------	---------------
+ *	00000001wxyza			000wxyz
+ *	0000001wxyzab			001wxyz
+ *	000001wxyzabc			010wxyz
+ *	00001wxyzabcd			011wxyz
+ *	0001wxyzabcde			100wxyz
+ *	001wxyzabcdef			101wxyz
+ *	01wxyzabcdefg			110wxyz
+ *	1wxyzabcdefgh			111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz.  * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+PJ_DEF(unsigned char) pjmedia_linear2ulaw(
+	int		pcm_val)	/* 2's complement (16-bit range) */
+{
+	int		mask;
+	int		seg;
+	unsigned char	uval;
+
+	/* Get the sign and the magnitude of the value. */
+	if (pcm_val < 0) {
+		pcm_val = BIAS - pcm_val;
+		mask = 0x7F;
+	} else {
+		pcm_val += BIAS;
+		mask = 0xFF;
+	}
+
+	/* Convert the scaled magnitude to segment number. */
+	seg = search(pcm_val, seg_end, 8);
+
+	/*
+	 * Combine the sign, segment, quantization bits;
+	 * and complement the code word.
+	 */
+	if (seg >= 8)		/* out of range, return maximum value. */
+		return (0x7F ^ mask);
+	else {
+		uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
+		return (uval ^ mask);
+	}
+
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+PJ_DEF(int) pjmedia_ulaw2linear(
+	unsigned char	u_val)
+{
+	int		t;
+
+	/* Shortcut: when input is zero, output is zero 
+	 * This will also make the VAD works harder.
+	 *  -bennylp
+	 */
+	if (u_val == 0) return 0;
+
+	/* Complement to obtain normal u-law value. */
+	u_val = ~u_val;
+
+	/*
+	 * Extract and bias the quantization bits. Then
+	 * shift up by the segment number and subtract out the bias.
+	 */
+	t = ((u_val & QUANT_MASK) << 3) + BIAS;
+	t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+	return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+
+/* A-law to u-law conversion */
+PJ_DEF(unsigned char) pjmedia_alaw2ulaw(
+	unsigned char	aval)
+{
+	aval &= 0xff;
+	return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+	    (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/* u-law to A-law conversion */
+PJ_DEF(unsigned char) pjmedia_ulaw2alaw(
+	unsigned char	uval)
+{
+	uval &= 0xff;
+	return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+	    (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
+
+
+#endif	/* PJMEDIA_HAS_ALAW_ULAW_TABLE */
+
diff --git a/jni/pjproject-android/.svn/pristine/91/917e04ba05a114e2e1d82a68646c491f7def7ef8.svn-base b/jni/pjproject-android/.svn/pristine/91/917e04ba05a114e2e1d82a68646c491f7def7ef8.svn-base
new file mode 100644
index 0000000..9757161
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/91/917e04ba05a114e2e1d82a68646c491f7def7ef8.svn-base
@@ -0,0 +1,1681 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 
+ */
+#include <pjlib-util/resolver.h>
+#include <pjlib-util/errno.h>
+#include <pj/assert.h>
+#include <pj/ctype.h>
+#include <pj/except.h>
+#include <pj/hash.h>
+#include <pj/ioqueue.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/pool_buf.h>
+#include <pj/rand.h>
+#include <pj/string.h>
+#include <pj/sock.h>
+#include <pj/timer.h>
+
+
+#define THIS_FILE	    "resolver.c"
+
+
+/* Check that maximum DNS nameservers is not too large. 
+ * This has got todo with the datatype to index the nameserver in the query.
+ */
+#if PJ_DNS_RESOLVER_MAX_NS > 256
+#   error "PJ_DNS_RESOLVER_MAX_NS is too large (max=256)"
+#endif
+
+
+#define RES_HASH_TABLE_SIZE 127		/**< Hash table size (must be 2^n-1 */
+#define PORT		    53		/**< Default NS port.		    */
+#define Q_HASH_TABLE_SIZE   127		/**< Query hash table size	    */
+#define TIMER_SIZE	    127		/**< Initial number of timers.	    */
+#define MAX_FD		    3		/**< Maximum internal sockets.	    */
+
+#define RES_BUF_SZ	    PJ_DNS_RESOLVER_RES_BUF_SIZE
+#define UDPSZ		    PJ_DNS_RESOLVER_MAX_UDP_SIZE
+#define TMP_SZ		    PJ_DNS_RESOLVER_TMP_BUF_SIZE
+
+
+/* Nameserver state */
+enum ns_state
+{
+    STATE_PROBING,
+    STATE_ACTIVE,
+    STATE_BAD,
+};
+
+static const char *state_names[3] =
+{
+    "Probing",
+    "Active",
+    "Bad"
+};
+
+
+/* 
+ * Each nameserver entry.
+ * A name server is identified by its socket address (IP and port).
+ * Each NS will have a flag to indicate whether it's properly functioning.
+ */
+struct nameserver
+{
+    pj_sockaddr_in  addr;		/**< Server address.		    */
+
+    enum ns_state   state;		/**< Nameserver state.		    */
+    pj_time_val	    state_expiry;	/**< Time set next state.	    */
+    pj_time_val	    rt_delay;		/**< Response time.		    */
+    
+
+    /* For calculating rt_delay: */
+    pj_uint16_t	    q_id;		/**< Query ID.			    */
+    pj_time_val	    sent_time;		/**< Time this query is sent.	    */
+};
+
+
+/* Child query list head 
+ * See comments on pj_dns_async_query below.
+ */
+struct query_head
+{
+    PJ_DECL_LIST_MEMBER(pj_dns_async_query);
+};
+
+
+/* Key to look for outstanding query and/or cached response */
+struct res_key
+{
+    pj_uint16_t		     qtype;		    /**< Query type.	    */
+    char		     name[PJ_MAX_HOSTNAME]; /**< Name being queried */
+};
+
+
+/* 
+ * This represents each asynchronous query entry.
+ * This entry will be put in two hash tables, the first one keyed on the DNS 
+ * transaction ID to match response with the query, and the second one keyed
+ * on "res_key" structure above to match a new request against outstanding 
+ * requests.
+ *
+ * An asynchronous entry may have child entries; child entries are subsequent
+ * queries to the same resource while there is pending query on the same
+ * DNS resource name and type. When a query has child entries, once the
+ * response is received (or error occurs), the response will trigger callback
+ * invocations for all childs entries.
+ *
+ * Note: when application cancels the query, the callback member will be
+ *       set to NULL, but for simplicity, the query will be let running.
+ */
+struct pj_dns_async_query
+{
+    PJ_DECL_LIST_MEMBER(pj_dns_async_query);	/**< List member.	    */
+
+    pj_dns_resolver	*resolver;	/**< The resolver instance.	    */
+    pj_uint16_t		 id;		/**< Transaction ID.		    */
+
+    unsigned		 transmit_cnt;	/**< Number of transmissions.	    */
+
+    struct res_key	 key;		/**< Key to index this query.	    */
+    pj_hash_entry_buf	 hbufid;	/**< Hash buffer 1		    */
+    pj_hash_entry_buf	 hbufkey;	/**< Hash buffer 2		    */
+    pj_timer_entry	 timer_entry;	/**< Timer to manage timeouts	    */
+    unsigned		 options;	/**< Query options.		    */
+    void		*user_data;	/**< Application data.		    */
+    pj_dns_callback	*cb;		/**< Callback to be called.	    */
+    struct query_head	 child_head;	/**< Child queries list head.	    */
+};
+
+
+/* This structure is used to keep cached response entry.
+ * The cache is a hash table keyed on "res_key" structure above.
+ */
+struct cached_res
+{
+    PJ_DECL_LIST_MEMBER(struct cached_res);
+
+    pj_pool_t		    *pool;	    /**< Cache's pool.		    */
+    struct res_key	     key;	    /**< Resource key.		    */
+    pj_hash_entry_buf	     hbuf;	    /**< Hash buffer		    */
+    pj_time_val		     expiry_time;   /**< Expiration time.	    */
+    pj_dns_parsed_packet    *pkt;	    /**< The response packet.	    */
+    unsigned		     ref_cnt;	    /**< Reference counter.	    */
+};
+
+
+/* Resolver entry */
+struct pj_dns_resolver
+{
+    pj_str_t		 name;		/**< Resolver instance name for id. */
+
+    /* Internals */
+    pj_pool_t		*pool;		/**< Internal pool.		    */
+    pj_mutex_t		*mutex;		/**< Mutex protection.		    */
+    pj_bool_t		 own_timer;	/**< Do we own timer?		    */
+    pj_timer_heap_t	*timer;		/**< Timer instance.		    */
+    pj_bool_t		 own_ioqueue;	/**< Do we own ioqueue?		    */
+    pj_ioqueue_t	*ioqueue;	/**< Ioqueue instance.		    */
+    char		 tmp_pool[TMP_SZ];/**< Temporary pool buffer.	    */
+
+    /* Socket */
+    pj_sock_t		 udp_sock;	/**< UDP socket.		    */
+    pj_ioqueue_key_t	*udp_key;	/**< UDP socket ioqueue key.	    */
+    unsigned char	 udp_rx_pkt[UDPSZ];/**< UDP receive buffer.	    */
+    unsigned char	 udp_tx_pkt[UDPSZ];/**< UDP receive buffer.	    */
+    pj_ssize_t		 udp_len;	/**< Length of received packet.	    */
+    pj_ioqueue_op_key_t	 udp_op_rx_key;	/**< UDP read operation key.	    */
+    pj_ioqueue_op_key_t	 udp_op_tx_key;	/**< UDP write operation key.	    */
+    pj_sockaddr_in	 udp_src_addr;	/**< Source address of packet	    */
+    int			 udp_addr_len;	/**< Source address length.	    */
+
+    /* Settings */
+    pj_dns_settings	 settings;	/**< Resolver settings.		    */
+
+    /* Nameservers */
+    unsigned		 ns_count;	/**< Number of name servers.	    */
+    struct nameserver	 ns[PJ_DNS_RESOLVER_MAX_NS];	/**< Array of NS.   */
+
+    /* Last DNS transaction ID used. */
+    pj_uint16_t		 last_id;
+
+    /* Hash table for cached response */
+    pj_hash_table_t	*hrescache;	/**< Cached response in hash table  */
+
+    /* Pending asynchronous query, hashed by transaction ID. */
+    pj_hash_table_t	*hquerybyid;
+
+    /* Pending asynchronous query, hashed by "res_key" */
+    pj_hash_table_t	*hquerybyres;
+
+    /* Query entries free list */
+    struct query_head	 query_free_nodes;
+};
+
+
+/* Callback from ioqueue when packet is received */
+static void on_read_complete(pj_ioqueue_key_t *key, 
+                             pj_ioqueue_op_key_t *op_key, 
+                             pj_ssize_t bytes_read);
+
+/* Callback to be called when query has timed out */
+static void on_timeout( pj_timer_heap_t *timer_heap,
+			struct pj_timer_entry *entry);
+
+/* Select which nameserver to use */
+static pj_status_t select_nameservers(pj_dns_resolver *resolver,
+				      unsigned *count,
+				      unsigned servers[]);
+
+
+/* Close UDP socket */
+static void close_sock(pj_dns_resolver *resv)
+{
+    /* Close existing socket */
+    if (resv->udp_key != NULL) {
+	pj_ioqueue_unregister(resv->udp_key);
+	resv->udp_key = NULL;
+	resv->udp_sock = PJ_INVALID_SOCKET;
+    } else if (resv->udp_sock != PJ_INVALID_SOCKET) {
+	pj_sock_close(resv->udp_sock);
+	resv->udp_sock = PJ_INVALID_SOCKET;
+    }
+}
+
+
+/* Initialize UDP socket */
+static pj_status_t init_sock(pj_dns_resolver *resv)
+{
+    pj_ioqueue_callback socket_cb;
+    pj_status_t status;
+
+    /* Create the UDP socket */
+    status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &resv->udp_sock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Bind to any address/port */
+    status = pj_sock_bind_in(resv->udp_sock, 0, 0);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Register to ioqueue */
+    pj_bzero(&socket_cb, sizeof(socket_cb));
+    socket_cb.on_read_complete = &on_read_complete;
+    status = pj_ioqueue_register_sock(resv->pool, resv->ioqueue,
+				      resv->udp_sock, resv, &socket_cb,
+				      &resv->udp_key);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pj_ioqueue_op_key_init(&resv->udp_op_rx_key, sizeof(resv->udp_op_rx_key));
+    pj_ioqueue_op_key_init(&resv->udp_op_tx_key, sizeof(resv->udp_op_tx_key));
+
+    /* Start asynchronous read to the UDP socket */
+    resv->udp_len = sizeof(resv->udp_rx_pkt);
+    resv->udp_addr_len = sizeof(resv->udp_src_addr);
+    status = pj_ioqueue_recvfrom(resv->udp_key, &resv->udp_op_rx_key,
+				 resv->udp_rx_pkt, &resv->udp_len,
+				 PJ_IOQUEUE_ALWAYS_ASYNC,
+				 &resv->udp_src_addr, &resv->udp_addr_len);
+    if (status != PJ_EPENDING)
+	return status;
+
+    return PJ_SUCCESS;
+}
+
+
+/* Initialize DNS settings with default values */
+PJ_DEF(void) pj_dns_settings_default(pj_dns_settings *s)
+{
+    pj_bzero(s, sizeof(pj_dns_settings));
+    s->qretr_delay = PJ_DNS_RESOLVER_QUERY_RETRANSMIT_DELAY;
+    s->qretr_count = PJ_DNS_RESOLVER_QUERY_RETRANSMIT_COUNT;
+    s->cache_max_ttl = PJ_DNS_RESOLVER_MAX_TTL;
+    s->good_ns_ttl = PJ_DNS_RESOLVER_GOOD_NS_TTL;
+    s->bad_ns_ttl = PJ_DNS_RESOLVER_BAD_NS_TTL;
+}
+
+
+/*
+ * Create the resolver.
+ */
+PJ_DEF(pj_status_t) pj_dns_resolver_create( pj_pool_factory *pf,
+					    const char *name,
+					    unsigned options,
+					    pj_timer_heap_t *timer,
+					    pj_ioqueue_t *ioqueue,
+					    pj_dns_resolver **p_resolver)
+{
+    pj_pool_t *pool;
+    pj_dns_resolver *resv;
+    pj_status_t status;
+
+    /* Sanity check */
+    PJ_ASSERT_RETURN(pf && p_resolver, PJ_EINVAL);
+
+    if (name == NULL)
+	name = THIS_FILE;
+
+    /* Create and initialize resolver instance */
+    pool = pj_pool_create(pf, name, 4000, 4000, NULL);
+    if (!pool)
+	return PJ_ENOMEM;
+
+    /* Create pool and name */
+    resv = PJ_POOL_ZALLOC_T(pool, struct pj_dns_resolver);
+    resv->pool = pool;
+    resv->udp_sock = PJ_INVALID_SOCKET;
+    pj_strdup2_with_null(pool, &resv->name, name);
+    
+    /* Create the mutex */
+    status = pj_mutex_create_recursive(pool, name, &resv->mutex);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Timer, ioqueue, and settings */
+    resv->timer = timer;
+    resv->ioqueue = ioqueue;
+    resv->last_id = 1;
+
+    pj_dns_settings_default(&resv->settings);
+    resv->settings.options = options;
+
+    /* Create the timer heap if one is not specified */
+    if (resv->timer == NULL) {
+	status = pj_timer_heap_create(pool, TIMER_SIZE, &resv->timer);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+    }
+
+    /* Create the ioqueue if one is not specified */
+    if (resv->ioqueue == NULL) {
+	status = pj_ioqueue_create(pool, MAX_FD, &resv->ioqueue);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+    }
+
+    /* Response cache hash table */
+    resv->hrescache = pj_hash_create(pool, RES_HASH_TABLE_SIZE);
+
+    /* Query hash table and free list. */
+    resv->hquerybyid = pj_hash_create(pool, Q_HASH_TABLE_SIZE);
+    resv->hquerybyres = pj_hash_create(pool, Q_HASH_TABLE_SIZE);
+    pj_list_init(&resv->query_free_nodes);
+
+    /* Initialize the UDP socket */
+    status = init_sock(resv);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Looks like everything is okay */
+    *p_resolver = resv;
+    return PJ_SUCCESS;
+
+on_error:
+    pj_dns_resolver_destroy(resv, PJ_FALSE);
+    return status;
+}
+
+
+/*
+ * Destroy DNS resolver instance.
+ */
+PJ_DEF(pj_status_t) pj_dns_resolver_destroy( pj_dns_resolver *resolver,
+					     pj_bool_t notify)
+{
+    pj_hash_iterator_t it_buf, *it;
+    PJ_ASSERT_RETURN(resolver, PJ_EINVAL);
+
+    if (notify) {
+	/*
+	 * Notify pending queries if requested.
+	 */
+	it = pj_hash_first(resolver->hquerybyid, &it_buf);
+	while (it) {
+	    pj_dns_async_query *q = (pj_dns_async_query *)
+	    			    pj_hash_this(resolver->hquerybyid, it);
+	    pj_dns_async_query *cq;
+	    if (q->cb)
+		(*q->cb)(q->user_data, PJ_ECANCELLED, NULL);
+
+	    cq = q->child_head.next;
+	    while (cq != (pj_dns_async_query*)&q->child_head) {
+		if (cq->cb)
+		    (*cq->cb)(cq->user_data, PJ_ECANCELLED, NULL);
+		cq = cq->next;
+	    }
+	    it = pj_hash_next(resolver->hquerybyid, it);
+	}
+    }
+
+    /* Destroy cached entries */
+    it = pj_hash_first(resolver->hrescache, &it_buf);
+    while (it) {
+	struct cached_res *cache;
+
+	cache = (struct cached_res*) pj_hash_this(resolver->hrescache, it);
+	pj_hash_set(NULL, resolver->hrescache, &cache->key, 
+		    sizeof(cache->key), 0, NULL);
+	pj_pool_release(cache->pool);
+
+	it = pj_hash_first(resolver->hrescache, &it_buf);
+    }
+
+    if (resolver->own_timer && resolver->timer) {
+	pj_timer_heap_destroy(resolver->timer);
+	resolver->timer = NULL;
+    }
+
+    close_sock(resolver);
+
+    if (resolver->own_ioqueue && resolver->ioqueue) {
+	pj_ioqueue_destroy(resolver->ioqueue);
+	resolver->ioqueue = NULL;
+    }
+
+    if (resolver->mutex) {
+	pj_mutex_destroy(resolver->mutex);
+	resolver->mutex = NULL;
+    }
+
+    if (resolver->pool) {
+	pj_pool_t *pool = resolver->pool;
+	resolver->pool = NULL;
+	pj_pool_release(pool);
+    }
+    return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Configure name servers for the DNS resolver. 
+ */
+PJ_DEF(pj_status_t) pj_dns_resolver_set_ns( pj_dns_resolver *resolver,
+					    unsigned count,
+					    const pj_str_t servers[],
+					    const pj_uint16_t ports[])
+{
+    unsigned i;
+    pj_time_val now;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(resolver && count && servers, PJ_EINVAL);
+    PJ_ASSERT_RETURN(count < PJ_DNS_RESOLVER_MAX_NS, PJ_EINVAL);
+
+    pj_mutex_lock(resolver->mutex);
+
+    if (count > PJ_DNS_RESOLVER_MAX_NS)
+	count = PJ_DNS_RESOLVER_MAX_NS;
+
+    resolver->ns_count = 0;
+    pj_bzero(resolver->ns, sizeof(resolver->ns));
+
+    pj_gettimeofday(&now);
+
+    for (i=0; i<count; ++i) {
+	struct nameserver *ns = &resolver->ns[i];
+
+	status = pj_sockaddr_in_init(&ns->addr, &servers[i], 
+				     (pj_uint16_t)(ports ? ports[i] : PORT));
+	if (status != PJ_SUCCESS) {
+	    pj_mutex_unlock(resolver->mutex);
+	    return PJLIB_UTIL_EDNSINNSADDR;
+	}
+
+	ns->state = STATE_ACTIVE;
+	ns->state_expiry = now;
+	ns->rt_delay.sec = 10;
+    }
+    
+    resolver->ns_count = count;
+
+    pj_mutex_unlock(resolver->mutex);
+    return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Modify the resolver settings.
+ */
+PJ_DEF(pj_status_t) pj_dns_resolver_set_settings(pj_dns_resolver *resolver,
+						 const pj_dns_settings *st)
+{
+    PJ_ASSERT_RETURN(resolver && st, PJ_EINVAL);
+
+    pj_mutex_lock(resolver->mutex);
+    pj_memcpy(&resolver->settings, st, sizeof(*st));
+    pj_mutex_unlock(resolver->mutex);
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get the resolver current settings.
+ */
+PJ_DEF(pj_status_t) pj_dns_resolver_get_settings( pj_dns_resolver *resolver,
+						  pj_dns_settings *st)
+{
+    PJ_ASSERT_RETURN(resolver && st, PJ_EINVAL);
+
+    pj_mutex_lock(resolver->mutex);
+    pj_memcpy(st, &resolver->settings, sizeof(*st));
+    pj_mutex_unlock(resolver->mutex);
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Poll for events from the resolver. 
+ */
+PJ_DEF(void) pj_dns_resolver_handle_events(pj_dns_resolver *resolver,
+					   const pj_time_val *timeout)
+{
+    PJ_ASSERT_ON_FAIL(resolver, return);
+
+    pj_mutex_lock(resolver->mutex);
+    pj_timer_heap_poll(resolver->timer, NULL);
+    pj_mutex_unlock(resolver->mutex);
+
+    pj_ioqueue_poll(resolver->ioqueue, timeout);
+}
+
+
+/* Get one query node from the free node, if any, or allocate 
+ * a new one.
+ */
+static pj_dns_async_query *alloc_qnode(pj_dns_resolver *resolver,
+				       unsigned options,
+				       void *user_data,
+				       pj_dns_callback *cb)
+{
+    pj_dns_async_query *q;
+
+    /* Merge query options with resolver options */
+    options |= resolver->settings.options;
+
+    if (!pj_list_empty(&resolver->query_free_nodes)) {
+	q = resolver->query_free_nodes.next;
+	pj_list_erase(q);
+	pj_bzero(q, sizeof(*q));
+    } else {
+	q = PJ_POOL_ZALLOC_T(resolver->pool, pj_dns_async_query);
+    }
+
+    /* Init query */
+    q->resolver = resolver;
+    q->options = options;
+    q->user_data = user_data;
+    q->cb = cb;
+    pj_list_init(&q->child_head);
+
+    return q;
+}
+
+
+/*
+ * Transmit query.
+ */
+static pj_status_t transmit_query(pj_dns_resolver *resolver,
+				  pj_dns_async_query *q)
+{
+    unsigned pkt_size;
+    unsigned i, server_cnt;
+    unsigned servers[PJ_DNS_RESOLVER_MAX_NS];
+    pj_time_val now;
+    pj_str_t name;
+    pj_time_val delay;
+    pj_status_t status;
+
+    /* Select which nameserver(s) to send requests to. */
+    server_cnt = PJ_ARRAY_SIZE(servers);
+    status = select_nameservers(resolver, &server_cnt, servers);
+    if (status != PJ_SUCCESS) {
+	return status;
+    }
+
+    if (server_cnt == 0) {
+	return PJLIB_UTIL_EDNSNOWORKINGNS;
+    }
+
+    /* Start retransmit/timeout timer for the query */
+    pj_assert(q->timer_entry.id == 0);
+    q->timer_entry.id = 1;
+    q->timer_entry.user_data = q;
+    q->timer_entry.cb = &on_timeout;
+
+    delay.sec = 0;
+    delay.msec = resolver->settings.qretr_delay;
+    pj_time_val_normalize(&delay);
+    status = pj_timer_heap_schedule(resolver->timer, &q->timer_entry, &delay);
+    if (status != PJ_SUCCESS) {
+	return status;
+    }
+
+    /* Check if the socket is available for sending */
+    if (pj_ioqueue_is_pending(resolver->udp_key, &resolver->udp_op_tx_key)) {
+	++q->transmit_cnt;
+	PJ_LOG(4,(resolver->name.ptr,
+		  "Socket busy in transmitting DNS %s query for %s%s",
+		  pj_dns_get_type_name(q->key.qtype),
+		  q->key.name,
+		  (q->transmit_cnt < resolver->settings.qretr_count?
+		   ", will try again later":"")));
+	return PJ_SUCCESS;
+    }
+
+    /* Create DNS query packet */
+    pkt_size = sizeof(resolver->udp_tx_pkt);
+    name = pj_str(q->key.name);
+    status = pj_dns_make_query(resolver->udp_tx_pkt, &pkt_size,
+			       q->id, q->key.qtype, &name);
+    if (status != PJ_SUCCESS) {
+	pj_timer_heap_cancel(resolver->timer, &q->timer_entry);
+	return status;
+    }
+
+    /* Get current time. */
+    pj_gettimeofday(&now);
+
+    /* Send the packet to name servers */
+    for (i=0; i<server_cnt; ++i) {
+	pj_ssize_t sent  = (pj_ssize_t) pkt_size;
+	struct nameserver *ns = &resolver->ns[servers[i]];
+
+	status = pj_ioqueue_sendto(resolver->udp_key,
+				   &resolver->udp_op_tx_key,
+				   resolver->udp_tx_pkt, &sent, 0,
+				   &resolver->ns[servers[i]].addr,
+				   sizeof(pj_sockaddr_in));
+
+	PJ_PERROR(4,(resolver->name.ptr, status,
+		  "%s %d bytes to NS %d (%s:%d): DNS %s query for %s",
+		  (q->transmit_cnt==0? "Transmitting":"Re-transmitting"),
+		  (int)pkt_size, servers[i],
+		  pj_inet_ntoa(ns->addr.sin_addr), 
+		  (int)pj_ntohs(ns->addr.sin_port),
+		  pj_dns_get_type_name(q->key.qtype), 
+		  q->key.name));
+
+	if (ns->q_id == 0) {
+	    ns->q_id = q->id;
+	    ns->sent_time = now;
+	}
+    }
+
+    ++q->transmit_cnt;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Initialize resource key for hash table lookup.
+ */
+static void init_res_key(struct res_key *key, int type, const pj_str_t *name)
+{
+    unsigned i;
+    pj_size_t len;
+    char *dst = key->name;
+    const char *src = name->ptr;
+
+    pj_bzero(key, sizeof(struct res_key));
+    key->qtype = (pj_uint16_t)type;
+
+    len = name->slen;
+    if (len > PJ_MAX_HOSTNAME) len = PJ_MAX_HOSTNAME;
+
+    /* Copy key, in lowercase */
+    for (i=0; i<len; ++i) {
+	*dst++ = (char)pj_tolower(*src++);
+    }
+}
+
+
+/* Allocate new cache entry */
+static struct cached_res *alloc_entry(pj_dns_resolver *resolver)
+{
+    pj_pool_t *pool;
+    struct cached_res *cache;
+
+    pool = pj_pool_create(resolver->pool->factory, "dnscache",
+			  RES_BUF_SZ, 256, NULL);
+    cache = PJ_POOL_ZALLOC_T(pool, struct cached_res);
+    cache->pool = pool;
+    cache->ref_cnt = 1;
+
+    return cache;
+}
+
+/* Re-allocate cache entry, to free cached packet */
+static void reset_entry(struct cached_res **p_cached)
+{
+    pj_pool_t *pool;
+    struct cached_res *cache = *p_cached;
+    unsigned ref_cnt;
+
+    pool = cache->pool;
+    ref_cnt = cache->ref_cnt;
+
+    pj_pool_reset(pool);
+
+    cache = PJ_POOL_ZALLOC_T(pool, struct cached_res);
+    cache->pool = pool;
+    cache->ref_cnt = ref_cnt;
+    *p_cached = cache;
+}
+
+/* Put unused/expired cached entry to the free list */
+static void free_entry(pj_dns_resolver *resolver, struct cached_res *cache)
+{
+    PJ_UNUSED_ARG(resolver);
+    pj_pool_release(cache->pool);
+}
+
+
+/*
+ * Create and start asynchronous DNS query for a single resource.
+ */
+PJ_DEF(pj_status_t) pj_dns_resolver_start_query( pj_dns_resolver *resolver,
+						 const pj_str_t *name,
+						 int type,
+						 unsigned options,
+						 pj_dns_callback *cb,
+						 void *user_data,
+						 pj_dns_async_query **p_query)
+{
+    pj_time_val now;
+    struct res_key key;
+    struct cached_res *cache;
+    pj_dns_async_query *q;
+    pj_uint32_t hval;
+    pj_status_t status = PJ_SUCCESS;
+
+    /* Validate arguments */
+    PJ_ASSERT_RETURN(resolver && name && type, PJ_EINVAL);
+
+    /* Check name is not too long. */
+    PJ_ASSERT_RETURN(name->slen>0 && name->slen < PJ_MAX_HOSTNAME,
+		     PJ_ENAMETOOLONG);
+
+    /* Check type */
+    PJ_ASSERT_RETURN(type > 0 && type < 0xFFFF, PJ_EINVAL);
+
+    if (p_query)
+	*p_query = NULL;
+
+    /* Build resource key for looking up hash tables */
+    init_res_key(&key, type, name);
+
+    /* Start working with the resolver */
+    pj_mutex_lock(resolver->mutex);
+
+    /* Get current time. */
+    pj_gettimeofday(&now);
+
+    /* First, check if we have cached response for the specified name/type,
+     * and the cached entry has not expired.
+     */
+    hval = 0;
+    cache = (struct cached_res *) pj_hash_get(resolver->hrescache, &key, 
+    					      sizeof(key), &hval);
+    if (cache) {
+	/* We've found a cached entry. */
+
+	/* Check for expiration */
+	if (PJ_TIME_VAL_GT(cache->expiry_time, now)) {
+
+	    /* Log */
+	    PJ_LOG(5,(resolver->name.ptr, 
+		      "Picked up DNS %s record for %.*s from cache, ttl=%d",
+		      pj_dns_get_type_name(type),
+		      (int)name->slen, name->ptr,
+		      (int)(cache->expiry_time.sec - now.sec)));
+
+	    /* Map DNS Rcode in the response into PJLIB status name space */
+	    status = PJ_DNS_GET_RCODE(cache->pkt->hdr.flags);
+	    status = PJ_STATUS_FROM_DNS_RCODE(status);
+
+	    /* Workaround for deadlock problem. Need to increment the cache's
+	     * ref counter first before releasing mutex, so the cache won't be
+	     * destroyed by other thread while in callback.
+	     */
+	    cache->ref_cnt++;
+	    pj_mutex_unlock(resolver->mutex);
+
+	    /* This cached response is still valid. Just return this
+	     * response to caller.
+	     */
+	    if (cb) {
+		(*cb)(user_data, status, cache->pkt);
+	    }
+
+	    /* Done. No host resolution is necessary */
+	    pj_mutex_lock(resolver->mutex);
+
+	    /* Decrement the ref counter. Also check if it is time to free
+	     * the cache (as it has been expired).
+	     */
+	    cache->ref_cnt--;
+	    if (cache->ref_cnt <= 0)
+		free_entry(resolver, cache);
+
+	    /* Must return PJ_SUCCESS */
+	    status = PJ_SUCCESS;
+
+	    goto on_return;
+	}
+
+	/* At this point, we have a cached entry, but this entry has expired.
+	 * Remove this entry from the cached list.
+	 */
+	pj_hash_set(NULL, resolver->hrescache, &key, sizeof(key), 0, NULL);
+
+	/* Also free the cache, if it is not being used (by callback). */
+	cache->ref_cnt--;
+	if (cache->ref_cnt <= 0)
+	    free_entry(resolver, cache);
+
+	/* Must continue with creating a query now */
+    }
+
+    /* Next, check if we have pending query on the same resource */
+    q = (pj_dns_async_query *) pj_hash_get(resolver->hquerybyres, &key, 
+    					   sizeof(key), NULL);
+    if (q) {
+	/* Yes, there's another pending query to the same key.
+	 * Just create a new child query and add this query to
+	 * pending query's child queries.
+	 */
+	pj_dns_async_query *nq;
+
+	nq = alloc_qnode(resolver, options, user_data, cb);
+	pj_list_push_back(&q->child_head, nq);
+
+	/* Done. This child query will be notified once the "parent"
+	 * query completes.
+	 */
+	status = PJ_SUCCESS;
+	goto on_return;
+    } 
+
+    /* There's no pending query to the same key, initiate a new one. */
+    q = alloc_qnode(resolver, options, user_data, cb);
+
+    /* Save the ID and key */
+    /* TODO: dnsext-forgery-resilient: randomize id for security */
+    q->id = resolver->last_id++;
+    if (resolver->last_id == 0)
+	resolver->last_id = 1;
+    pj_memcpy(&q->key, &key, sizeof(struct res_key));
+
+    /* Send the query */
+    status = transmit_query(resolver, q);
+    if (status != PJ_SUCCESS) {
+	pj_list_push_back(&resolver->query_free_nodes, q);
+	goto on_return;
+    }
+
+    /* Add query entry to the hash tables */
+    pj_hash_set_np(resolver->hquerybyid, &q->id, sizeof(q->id), 
+		   0, q->hbufid, q);
+    pj_hash_set_np(resolver->hquerybyres, &q->key, sizeof(q->key),
+		   0, q->hbufkey, q);
+
+    if (p_query)
+	*p_query = q;
+
+on_return:
+    pj_mutex_unlock(resolver->mutex);
+    return status;
+}
+
+
+/*
+ * Cancel a pending query.
+ */
+PJ_DEF(pj_status_t) pj_dns_resolver_cancel_query(pj_dns_async_query *query,
+						 pj_bool_t notify)
+{
+    pj_dns_callback *cb;
+
+    PJ_ASSERT_RETURN(query, PJ_EINVAL);
+
+    pj_mutex_lock(query->resolver->mutex);
+
+    cb = query->cb;
+    query->cb = NULL;
+
+    if (notify)
+	(*cb)(query->user_data, PJ_ECANCELLED, NULL);
+
+    pj_mutex_unlock(query->resolver->mutex);
+    return PJ_SUCCESS;
+}
+
+
+/* 
+ * DNS response containing A packet. 
+ */
+PJ_DEF(pj_status_t) pj_dns_parse_a_response(const pj_dns_parsed_packet *pkt,
+					    pj_dns_a_record *rec)
+{
+    enum { MAX_SEARCH = 20 };
+    pj_str_t hostname, alias = {NULL, 0}, *resname;
+    pj_size_t bufstart = 0;
+    pj_size_t bufleft = sizeof(rec->buf_);
+    unsigned i, ansidx, search_cnt=0;
+
+    PJ_ASSERT_RETURN(pkt && rec, PJ_EINVAL);
+
+    /* Init the record */
+    pj_bzero(rec, sizeof(pj_dns_a_record));
+
+    /* Return error if there's error in the packet. */
+    if (PJ_DNS_GET_RCODE(pkt->hdr.flags))
+	return PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_GET_RCODE(pkt->hdr.flags));
+
+    /* Return error if there's no query section */
+    if (pkt->hdr.qdcount == 0)
+	return PJLIB_UTIL_EDNSINANSWER;
+
+    /* Return error if there's no answer */
+    if (pkt->hdr.anscount == 0)
+	return PJLIB_UTIL_EDNSNOANSWERREC;
+
+    /* Get the hostname from the query. */
+    hostname = pkt->q[0].name;
+
+    /* Copy hostname to the record */
+    if (hostname.slen > (int)bufleft) {
+	return PJ_ENAMETOOLONG;
+    }
+
+    pj_memcpy(&rec->buf_[bufstart], hostname.ptr, hostname.slen);
+    rec->name.ptr = &rec->buf_[bufstart];
+    rec->name.slen = hostname.slen;
+
+    bufstart += hostname.slen;
+    bufleft -= hostname.slen;
+
+    /* Find the first RR which name matches the hostname */
+    for (ansidx=0; ansidx < pkt->hdr.anscount; ++ansidx) {
+	if (pj_stricmp(&pkt->ans[ansidx].name, &hostname)==0)
+	    break;
+    }
+
+    if (ansidx == pkt->hdr.anscount)
+	return PJLIB_UTIL_EDNSNOANSWERREC;
+
+    resname = &hostname;
+
+    /* Keep following CNAME records. */
+    while (pkt->ans[ansidx].type == PJ_DNS_TYPE_CNAME &&
+	   search_cnt++ < MAX_SEARCH)
+    {
+	resname = &pkt->ans[ansidx].rdata.cname.name;
+
+	if (!alias.slen)
+	    alias = *resname;
+
+	for (i=0; i < pkt->hdr.anscount; ++i) {
+	    if (pj_stricmp(resname, &pkt->ans[i].name)==0) {
+		break;
+	    }
+	}
+
+	if (i==pkt->hdr.anscount)
+	    return PJLIB_UTIL_EDNSNOANSWERREC;
+
+	ansidx = i;
+    }
+
+    if (search_cnt >= MAX_SEARCH)
+	return PJLIB_UTIL_EDNSINANSWER;
+
+    if (pkt->ans[ansidx].type != PJ_DNS_TYPE_A)
+	return PJLIB_UTIL_EDNSINANSWER;
+
+    /* Copy alias to the record, if present. */
+    if (alias.slen) {
+	if (alias.slen > (int)bufleft)
+	    return PJ_ENAMETOOLONG;
+
+	pj_memcpy(&rec->buf_[bufstart], alias.ptr, alias.slen);
+	rec->alias.ptr = &rec->buf_[bufstart];
+	rec->alias.slen = alias.slen;
+
+	bufstart += alias.slen;
+	bufleft -= alias.slen;
+    }
+
+    /* Get the IP addresses. */
+    for (i=0; i < pkt->hdr.anscount; ++i) {
+	if (pkt->ans[i].type == PJ_DNS_TYPE_A &&
+	    pj_stricmp(&pkt->ans[i].name, resname)==0 &&
+	    rec->addr_count < PJ_DNS_MAX_IP_IN_A_REC)
+	{
+	    rec->addr[rec->addr_count++].s_addr =
+		pkt->ans[i].rdata.a.ip_addr.s_addr;
+	}
+    }
+
+    if (rec->addr_count == 0)
+	return PJLIB_UTIL_EDNSNOANSWERREC;
+
+    return PJ_SUCCESS;
+}
+
+
+/* Set nameserver state */
+static void set_nameserver_state(pj_dns_resolver *resolver,
+				 unsigned index,
+				 enum ns_state state,
+				 const pj_time_val *now)
+{
+    struct nameserver *ns = &resolver->ns[index];
+    enum ns_state old_state = ns->state;
+
+    ns->state = state;
+    ns->state_expiry = *now;
+
+    if (state == STATE_PROBING)
+	ns->state_expiry.sec += ((resolver->settings.qretr_count + 2) *
+				 resolver->settings.qretr_delay) / 1000;
+    else if (state == STATE_ACTIVE)
+	ns->state_expiry.sec += resolver->settings.good_ns_ttl;
+    else
+	ns->state_expiry.sec += resolver->settings.bad_ns_ttl;
+
+    PJ_LOG(5, (resolver->name.ptr, "Nameserver %s:%d state changed %s --> %s",
+	       pj_inet_ntoa(ns->addr.sin_addr),
+	       (int)pj_ntohs(ns->addr.sin_port),
+	       state_names[old_state], state_names[state]));
+}
+
+
+/* Select which nameserver(s) to use. Note this may return multiple
+ * name servers. The algorithm to select which nameservers to be
+ * sent the request to is as follows:
+ *  - select the first nameserver that is known to be good for the
+ *    last PJ_DNS_RESOLVER_GOOD_NS_TTL interval.
+ *  - for all NSes, if last_known_good >= PJ_DNS_RESOLVER_GOOD_NS_TTL, 
+ *    include the NS to re-check again that the server is still good,
+ *    unless the NS is known to be bad in the last PJ_DNS_RESOLVER_BAD_NS_TTL
+ *    interval.
+ *  - for all NSes, if last_known_bad >= PJ_DNS_RESOLVER_BAD_NS_TTL, 
+ *    also include the NS to re-check again that the server is still bad.
+ */
+static pj_status_t select_nameservers(pj_dns_resolver *resolver,
+				      unsigned *count,
+				      unsigned servers[])
+{
+    unsigned i, max_count=*count;
+    int min;
+    pj_time_val now;
+
+    pj_assert(max_count > 0);
+
+    *count = 0;
+    servers[0] = 0xFFFF;
+
+    /* Check that nameservers are configured. */
+    if (resolver->ns_count == 0)
+	return PJLIB_UTIL_EDNSNONS;
+
+    pj_gettimeofday(&now);
+
+    /* Select one Active nameserver with best response time. */
+    for (min=-1, i=0; i<resolver->ns_count; ++i) {
+	struct nameserver *ns = &resolver->ns[i];
+
+	if (ns->state != STATE_ACTIVE)
+	    continue;
+
+	if (min == -1)
+	    min = i;
+	else if (PJ_TIME_VAL_LT(ns->rt_delay, resolver->ns[min].rt_delay))
+	    min = i;
+    }
+    if (min != -1) {
+	servers[0] = min;
+	++(*count);
+    }
+
+    /* Scan nameservers. */
+    for (i=0; i<resolver->ns_count && *count < max_count; ++i) {
+	struct nameserver *ns = &resolver->ns[i];
+
+	if (PJ_TIME_VAL_LTE(ns->state_expiry, now)) {
+	    if (ns->state == STATE_PROBING) {
+		set_nameserver_state(resolver, i, STATE_BAD, &now);
+	    } else {
+		set_nameserver_state(resolver, i, STATE_PROBING, &now);
+		if ((int)i != min) {
+		    servers[*count] = i;
+		    ++(*count);
+		}
+	    }
+	} else if (ns->state == STATE_PROBING && (int)i != min) {
+	    servers[*count] = i;
+	    ++(*count);
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/* Update name server status */
+static void report_nameserver_status(pj_dns_resolver *resolver,
+				     const pj_sockaddr_in *ns_addr,
+				     const pj_dns_parsed_packet *pkt)
+{
+    unsigned i;
+    int rcode;
+    pj_uint32_t q_id;
+    pj_time_val now;
+    pj_bool_t is_good;
+
+    /* Only mark nameserver as "bad" if it returned non-parseable response or
+     * it returned the following status codes
+     */
+    if (pkt) {
+	rcode = PJ_DNS_GET_RCODE(pkt->hdr.flags);
+	q_id = pkt->hdr.id;
+    } else {
+	rcode = 0;
+	q_id = (pj_uint32_t)-1;
+    }
+
+    if (!pkt || rcode == PJ_DNS_RCODE_SERVFAIL ||
+	        rcode == PJ_DNS_RCODE_REFUSED ||
+	        rcode == PJ_DNS_RCODE_NOTAUTH) 
+    {
+	is_good = PJ_FALSE;
+    } else {
+	is_good = PJ_TRUE;
+    }
+
+
+    /* Mark time */
+    pj_gettimeofday(&now);
+
+    /* Recheck all nameservers. */
+    for (i=0; i<resolver->ns_count; ++i) {
+	struct nameserver *ns = &resolver->ns[i];
+
+	if (ns->addr.sin_addr.s_addr == ns_addr->sin_addr.s_addr &&
+	    ns->addr.sin_port == ns_addr->sin_port &&
+	    ns->addr.sin_family == ns_addr->sin_family)
+	{
+	    if (q_id == ns->q_id) {
+		/* Calculate response time */
+		pj_time_val rt = now;
+		PJ_TIME_VAL_SUB(rt, ns->sent_time);
+		ns->rt_delay = rt;
+		ns->q_id = 0;
+	    }
+	    set_nameserver_state(resolver, i, 
+				 (is_good ? STATE_ACTIVE : STATE_BAD), &now);
+	    break;
+	}
+    }
+}
+
+
+/* Update response cache */
+static void update_res_cache(pj_dns_resolver *resolver,
+			     const struct res_key *key,
+			     pj_status_t status,
+			     pj_bool_t set_expiry,
+			     const pj_dns_parsed_packet *pkt)
+{
+    struct cached_res *cache;
+    pj_uint32_t hval=0, ttl;
+
+    /* If status is unsuccessful, clear the same entry from the cache */
+    if (status != PJ_SUCCESS) {
+	cache = (struct cached_res *) pj_hash_get(resolver->hrescache, key, 
+						  sizeof(*key), &hval);
+	if (cache && --cache->ref_cnt <= 0)
+	    free_entry(resolver, cache);
+	pj_hash_set(NULL, resolver->hrescache, key, sizeof(*key), hval, NULL);
+    }
+
+
+    /* Calculate expiration time. */
+    if (set_expiry) {
+	if (pkt->hdr.anscount == 0 || status != PJ_SUCCESS) {
+	    /* If we don't have answers for the name, then give a different
+	     * ttl value (note: PJ_DNS_RESOLVER_INVALID_TTL may be zero, 
+	     * which means that invalid names won't be kept in the cache)
+	     */
+	    ttl = PJ_DNS_RESOLVER_INVALID_TTL;
+
+	} else {
+	    /* Otherwise get the minimum TTL from the answers */
+	    unsigned i;
+	    ttl = 0xFFFFFFFF;
+	    for (i=0; i<pkt->hdr.anscount; ++i) {
+		if (pkt->ans[i].ttl < ttl)
+		    ttl = pkt->ans[i].ttl;
+	    }
+	}
+    } else {
+	ttl = 0xFFFFFFFF;
+    }
+
+    /* Apply maximum TTL */
+    if (ttl > resolver->settings.cache_max_ttl)
+	ttl = resolver->settings.cache_max_ttl;
+
+    /* If TTL is zero, clear the same entry in the hash table */
+    if (ttl == 0) {
+	cache = (struct cached_res *) pj_hash_get(resolver->hrescache, key, 
+						  sizeof(*key), &hval);
+	if (cache && --cache->ref_cnt <= 0)
+	    free_entry(resolver, cache);
+	pj_hash_set(NULL, resolver->hrescache, key, sizeof(*key), hval, NULL);
+	return;
+    }
+
+    /* Get a cache response entry */
+    cache = (struct cached_res *) pj_hash_get(resolver->hrescache, key, 
+    					      sizeof(*key), &hval);
+    if (cache == NULL) {
+	cache = alloc_entry(resolver);
+    } else if (cache->ref_cnt > 1) {
+	/* When cache entry is being used by callback (to app), just decrement
+	 * ref_cnt so it will be freed after the callback returns and allocate
+	 * new entry.
+	 */
+	cache->ref_cnt--;
+	cache = alloc_entry(resolver);
+    } else {
+	/* Reset cache to avoid bloated cache pool */
+	reset_entry(&cache);
+    }
+
+    /* Duplicate the packet.
+     * We don't need to keep the NS and AR sections from the packet,
+     * so exclude from duplication. We do need to keep the Query
+     * section since DNS A parser needs the query section to know
+     * the name being requested.
+     */
+    pj_dns_packet_dup(cache->pool, pkt, 
+		      PJ_DNS_NO_NS | PJ_DNS_NO_AR,
+		      &cache->pkt);
+
+    /* Calculate expiration time */
+    if (set_expiry) {
+	pj_gettimeofday(&cache->expiry_time);
+	cache->expiry_time.sec += ttl;
+    } else {
+	cache->expiry_time.sec = 0x7FFFFFFFL;
+	cache->expiry_time.msec = 0;
+    }
+
+    /* Copy key to the cached response */
+    pj_memcpy(&cache->key, key, sizeof(*key));
+
+    /* Update the hash table */
+    pj_hash_set_np(resolver->hrescache, &cache->key, sizeof(*key), hval,
+		   cache->hbuf, cache);
+
+}
+
+
+/* Callback to be called when query has timed out */
+static void on_timeout( pj_timer_heap_t *timer_heap,
+			struct pj_timer_entry *entry)
+{
+    pj_dns_resolver *resolver;
+    pj_dns_async_query *q, *cq;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(timer_heap);
+
+    q = (pj_dns_async_query *) entry->user_data;
+    resolver = q->resolver;
+
+    pj_mutex_lock(resolver->mutex);
+
+    /* Recheck that this query is still pending, since there is a slight
+     * possibility of race condition (timer elapsed while at the same time
+     * response arrives)
+     */
+    if (pj_hash_get(resolver->hquerybyid, &q->id, sizeof(q->id), NULL)==NULL) {
+	/* Yeah, this query is done. */
+	pj_mutex_unlock(resolver->mutex);
+	return;
+    }
+
+    /* Invalidate id. */
+    q->timer_entry.id = 0;
+
+    /* Check to see if we should retransmit instead of time out */
+    if (q->transmit_cnt < resolver->settings.qretr_count) {
+	status = transmit_query(resolver, q);
+	if (status == PJ_SUCCESS) {
+	    pj_mutex_unlock(resolver->mutex);
+	    return;
+	} else {
+	    /* Error occurs */
+	    char errmsg[PJ_ERR_MSG_SIZE];
+
+	    pj_strerror(status, errmsg, sizeof(errmsg));
+	    PJ_LOG(4,(resolver->name.ptr,
+		      "Error transmitting request: %s", errmsg));
+
+	    /* Let it fallback to timeout section below */
+	}
+    }
+
+    /* Clear hash table entries */
+    pj_hash_set(NULL, resolver->hquerybyid, &q->id, sizeof(q->id), 0, NULL);
+    pj_hash_set(NULL, resolver->hquerybyres, &q->key, sizeof(q->key), 0, NULL);
+
+    /* Workaround for deadlock problem in #1565 (similar to #1108) */
+    pj_mutex_unlock(resolver->mutex);
+
+    /* Call application callback, if any. */
+    if (q->cb)
+	(*q->cb)(q->user_data, PJ_ETIMEDOUT, NULL);
+
+    /* Call application callback for child queries. */
+    cq = q->child_head.next;
+    while (cq != (void*)&q->child_head) {
+	if (cq->cb)
+	    (*cq->cb)(cq->user_data, PJ_ETIMEDOUT, NULL);
+	cq = cq->next;
+    }
+
+    /* Workaround for deadlock problem in #1565 (similar to #1108) */
+    pj_mutex_lock(resolver->mutex);
+
+    /* Clear data */
+    q->timer_entry.id = 0;
+    q->user_data = NULL;
+
+    /* Put child entries into recycle list */
+    cq = q->child_head.next;
+    while (cq != (void*)&q->child_head) {
+	pj_dns_async_query *next = cq->next;
+	pj_list_push_back(&resolver->query_free_nodes, cq);
+	cq = next;
+    }
+
+    /* Put query entry into recycle list */
+    pj_list_push_back(&resolver->query_free_nodes, q);
+
+    pj_mutex_unlock(resolver->mutex);
+}
+
+
+/* Callback from ioqueue when packet is received */
+static void on_read_complete(pj_ioqueue_key_t *key, 
+                             pj_ioqueue_op_key_t *op_key, 
+                             pj_ssize_t bytes_read)
+{
+    pj_dns_resolver *resolver;
+    pj_pool_t *pool = NULL;
+    pj_dns_parsed_packet *dns_pkt;
+    pj_dns_async_query *q;
+    pj_status_t status;
+    PJ_USE_EXCEPTION;
+
+
+    resolver = (pj_dns_resolver *) pj_ioqueue_get_user_data(key);
+    pj_mutex_lock(resolver->mutex);
+
+
+    /* Check for errors */
+    if (bytes_read < 0) {
+	char errmsg[PJ_ERR_MSG_SIZE];
+
+	status = (pj_status_t)-bytes_read;
+	pj_strerror(status, errmsg, sizeof(errmsg));
+	PJ_LOG(4,(resolver->name.ptr, 
+		  "DNS resolver read error from %s:%d: %s", 
+		  pj_inet_ntoa(resolver->udp_src_addr.sin_addr),
+		  pj_ntohs(resolver->udp_src_addr.sin_port),
+		  errmsg));
+
+	goto read_next_packet;
+    }
+
+    PJ_LOG(5,(resolver->name.ptr, 
+	      "Received %d bytes DNS response from %s:%d",
+	      (int)bytes_read, 
+	      pj_inet_ntoa(resolver->udp_src_addr.sin_addr),
+	      pj_ntohs(resolver->udp_src_addr.sin_port)));
+
+
+    /* Check for zero packet */
+    if (bytes_read == 0)
+	goto read_next_packet;
+
+    /* Create temporary pool from a fixed buffer */
+    pool = pj_pool_create_on_buf("restmp", resolver->tmp_pool, 
+				 sizeof(resolver->tmp_pool));
+
+    /* Parse DNS response */
+    status = -1;
+    dns_pkt = NULL;
+    PJ_TRY {
+	status = pj_dns_parse_packet(pool, resolver->udp_rx_pkt, 
+				     (unsigned)bytes_read, &dns_pkt);
+    }
+    PJ_CATCH_ANY {
+	status = PJ_ENOMEM;
+    }
+    PJ_END;
+
+    /* Update nameserver status */
+    report_nameserver_status(resolver, &resolver->udp_src_addr, dns_pkt);
+
+    /* Handle parse error */
+    if (status != PJ_SUCCESS) {
+	char errmsg[PJ_ERR_MSG_SIZE];
+
+	pj_strerror(status, errmsg, sizeof(errmsg));
+	PJ_LOG(3,(resolver->name.ptr, 
+		  "Error parsing DNS response from %s:%d: %s", 
+		  pj_inet_ntoa(resolver->udp_src_addr.sin_addr), 
+		  pj_ntohs(resolver->udp_src_addr.sin_port), 
+		  errmsg));
+	goto read_next_packet;
+    }
+
+    /* Find the query based on the transaction ID */
+    q = (pj_dns_async_query*) 
+        pj_hash_get(resolver->hquerybyid, &dns_pkt->hdr.id,
+		    sizeof(dns_pkt->hdr.id), NULL);
+    if (!q) {
+	PJ_LOG(5,(resolver->name.ptr, 
+		  "DNS response from %s:%d id=%d discarded",
+		  pj_inet_ntoa(resolver->udp_src_addr.sin_addr), 
+		  pj_ntohs(resolver->udp_src_addr.sin_port),
+		  (unsigned)dns_pkt->hdr.id));
+	goto read_next_packet;
+    }
+
+    /* Map DNS Rcode in the response into PJLIB status name space */
+    status = PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_GET_RCODE(dns_pkt->hdr.flags));
+
+    /* Cancel query timeout timer. */
+    pj_assert(q->timer_entry.id != 0);
+    pj_timer_heap_cancel(resolver->timer, &q->timer_entry);
+    q->timer_entry.id = 0;
+
+    /* Clear hash table entries */
+    pj_hash_set(NULL, resolver->hquerybyid, &q->id, sizeof(q->id), 0, NULL);
+    pj_hash_set(NULL, resolver->hquerybyres, &q->key, sizeof(q->key), 0, NULL);
+
+    /* Workaround for deadlock problem in #1108 */
+    pj_mutex_unlock(resolver->mutex);
+
+    /* Notify applications first, to allow application to modify the 
+     * record before it is saved to the hash table.
+     */
+    if (q->cb)
+	(*q->cb)(q->user_data, status, dns_pkt);
+
+    /* If query has subqueries, notify subqueries's application callback */
+    if (!pj_list_empty(&q->child_head)) {
+	pj_dns_async_query *child_q;
+
+	child_q = q->child_head.next;
+	while (child_q != (pj_dns_async_query*)&q->child_head) {
+	    if (child_q->cb)
+		(*child_q->cb)(child_q->user_data, status, dns_pkt);
+	    child_q = child_q->next;
+	}
+    }
+
+    /* Workaround for deadlock problem in #1108 */
+    pj_mutex_lock(resolver->mutex);
+
+    /* Save/update response cache. */
+    update_res_cache(resolver, &q->key, status, PJ_TRUE, dns_pkt);
+    
+    /* Recycle query objects, starting with the child queries */
+    if (!pj_list_empty(&q->child_head)) {
+	pj_dns_async_query *child_q;
+
+	child_q = q->child_head.next;
+	while (child_q != (pj_dns_async_query*)&q->child_head) {
+	    pj_dns_async_query *next = child_q->next;
+	    pj_list_erase(child_q);
+	    pj_list_push_back(&resolver->query_free_nodes, child_q);
+	    child_q = next;
+	}
+    }
+    pj_list_push_back(&resolver->query_free_nodes, q);
+
+read_next_packet:
+    if (pool) {
+	/* needed just in case PJ_HAS_POOL_ALT_API is set */
+	pj_pool_release(pool);
+    }
+    bytes_read = sizeof(resolver->udp_rx_pkt);
+    resolver->udp_addr_len = sizeof(resolver->udp_src_addr);
+    status = pj_ioqueue_recvfrom(resolver->udp_key, op_key, 
+				 resolver->udp_rx_pkt,
+				 &bytes_read, PJ_IOQUEUE_ALWAYS_ASYNC,
+				 &resolver->udp_src_addr, 
+				 &resolver->udp_addr_len);
+    if (status != PJ_EPENDING) {
+	char errmsg[PJ_ERR_MSG_SIZE];
+
+	pj_strerror(status, errmsg, sizeof(errmsg));	
+	PJ_LOG(4,(resolver->name.ptr, "DNS resolver ioqueue read error: %s",
+		  errmsg));
+
+	pj_assert(!"Unhandled error");
+    }
+
+    pj_mutex_unlock(resolver->mutex);
+}
+
+
+/*
+ * Put the specified DNS packet into DNS cache. This function is mainly used
+ * for testing the resolver, however it can also be used to inject entries
+ * into the resolver.
+ */
+PJ_DEF(pj_status_t) pj_dns_resolver_add_entry( pj_dns_resolver *resolver,
+					       const pj_dns_parsed_packet *pkt,
+					       pj_bool_t set_ttl)
+{
+    struct res_key key;
+
+    /* Sanity check */
+    PJ_ASSERT_RETURN(resolver && pkt, PJ_EINVAL);
+
+    /* Packet must be a DNS response */
+    PJ_ASSERT_RETURN(PJ_DNS_GET_QR(pkt->hdr.flags) & 1, PJ_EINVAL);
+
+    /* Make sure there are answers in the packet */
+    PJ_ASSERT_RETURN((pkt->hdr.anscount && pkt->ans) ||
+		      (pkt->hdr.qdcount && pkt->q),
+		     PJLIB_UTIL_EDNSNOANSWERREC);
+
+    pj_mutex_lock(resolver->mutex);
+
+    /* Build resource key for looking up hash tables */
+    pj_bzero(&key, sizeof(struct res_key));
+    if (pkt->hdr.anscount) {
+	/* Make sure name is not too long. */
+	PJ_ASSERT_RETURN(pkt->ans[0].name.slen < PJ_MAX_HOSTNAME, 
+			 PJ_ENAMETOOLONG);
+
+	init_res_key(&key, pkt->ans[0].type, &pkt->ans[0].name);
+
+    } else {
+	/* Make sure name is not too long. */
+	PJ_ASSERT_RETURN(pkt->q[0].name.slen < PJ_MAX_HOSTNAME, 
+			 PJ_ENAMETOOLONG);
+
+	init_res_key(&key, pkt->q[0].type, &pkt->q[0].name);
+    }
+
+    /* Insert entry. */
+    update_res_cache(resolver, &key, PJ_SUCCESS, set_ttl, pkt);
+
+    pj_mutex_unlock(resolver->mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get the total number of response in the response cache.
+ */
+PJ_DEF(unsigned) pj_dns_resolver_get_cached_count(pj_dns_resolver *resolver)
+{
+    unsigned count;
+
+    PJ_ASSERT_RETURN(resolver, 0);
+
+    pj_mutex_lock(resolver->mutex);
+    count = pj_hash_count(resolver->hrescache);
+    pj_mutex_unlock(resolver->mutex);
+
+    return count;
+}
+
+
+/*
+ * Dump resolver state to the log.
+ */
+PJ_DEF(void) pj_dns_resolver_dump(pj_dns_resolver *resolver,
+				  pj_bool_t detail)
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+    unsigned i;
+    pj_time_val now;
+
+    pj_mutex_lock(resolver->mutex);
+
+    pj_gettimeofday(&now);
+
+    PJ_LOG(3,(resolver->name.ptr, " Dumping resolver state:"));
+
+    PJ_LOG(3,(resolver->name.ptr, "  Name servers:"));
+    for (i=0; i<resolver->ns_count; ++i) {
+	const char *state_names[] = { "probing", "active", "bad"};
+	struct nameserver *ns = &resolver->ns[i];
+
+	PJ_LOG(3,(resolver->name.ptr,
+		  "   NS %d: %s:%d (state=%s until %ds, rtt=%d ms)",
+		  i, pj_inet_ntoa(ns->addr.sin_addr),
+		  pj_ntohs(ns->addr.sin_port),
+		  state_names[ns->state],
+		  ns->state_expiry.sec - now.sec,
+		  PJ_TIME_VAL_MSEC(ns->rt_delay)));
+    }
+
+    PJ_LOG(3,(resolver->name.ptr, "  Nb. of cached responses: %u",
+	      pj_hash_count(resolver->hrescache)));
+    if (detail) {
+	pj_hash_iterator_t itbuf, *it;
+	it = pj_hash_first(resolver->hrescache, &itbuf);
+	while (it) {
+	    struct cached_res *cache;
+	    cache = (struct cached_res*)pj_hash_this(resolver->hrescache, it);
+	    PJ_LOG(3,(resolver->name.ptr, 
+		      "   Type %s: %s",
+		      pj_dns_get_type_name(cache->key.qtype), 
+		      cache->key.name));
+	    it = pj_hash_next(resolver->hrescache, it);
+	}
+    }
+    PJ_LOG(3,(resolver->name.ptr, "  Nb. of pending queries: %u (%u)",
+	      pj_hash_count(resolver->hquerybyid),
+	      pj_hash_count(resolver->hquerybyres)));
+    if (detail) {
+	pj_hash_iterator_t itbuf, *it;
+	it = pj_hash_first(resolver->hquerybyid, &itbuf);
+	while (it) {
+	    struct pj_dns_async_query *q;
+	    q = (pj_dns_async_query*) pj_hash_this(resolver->hquerybyid, it);
+	    PJ_LOG(3,(resolver->name.ptr, 
+		      "   Type %s: %s",
+		      pj_dns_get_type_name(q->key.qtype), 
+		      q->key.name));
+	    it = pj_hash_next(resolver->hquerybyid, it);
+	}
+    }
+    PJ_LOG(3,(resolver->name.ptr, "  Nb. of pending query free nodes: %u",
+	      pj_list_size(&resolver->query_free_nodes)));
+    PJ_LOG(3,(resolver->name.ptr, "  Nb. of timer entries: %u",
+	      pj_timer_heap_count(resolver->timer)));
+    PJ_LOG(3,(resolver->name.ptr, "  Pool capacity: %d, used size: %d",
+	      pj_pool_get_capacity(resolver->pool),
+	      pj_pool_get_used_size(resolver->pool)));
+
+    pj_mutex_unlock(resolver->mutex);
+#endif
+}
+
diff --git a/jni/pjproject-android/.svn/pristine/91/91996d01421ac010fe26739f351849e63651d311.svn-base b/jni/pjproject-android/.svn/pristine/91/91996d01421ac010fe26739f351849e63651d311.svn-base
new file mode 100644
index 0000000..1949d05
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/91/91996d01421ac010fe26739f351849e63651d311.svn-base
@@ -0,0 +1,184 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 
+ */
+#include <pjlib-util/errno.h>
+#include <pjlib-util/types.h>
+#include <pj/assert.h>
+#include <pj/string.h>
+
+
+
+/* PJLIB_UTIL's own error codes/messages 
+ * MUST KEEP THIS ARRAY SORTED!!
+ * Message must be limited to 64 chars!
+ */
+#if defined(PJ_HAS_ERROR_STRING) && PJ_HAS_ERROR_STRING!=0
+static const struct 
+{
+    int code;
+    const char *msg;
+} err_str[] = 
+{
+    /* STUN errors */
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNRESOLVE,	"Unable to resolve STUN server" ),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNINMSGTYPE,	"Unknown STUN message type" ),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNINMSGLEN,	"Invalid STUN message length" ),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNINATTRLEN,	"STUN attribute length error" ),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNINATTRTYPE,	"Invalid STUN attribute type" ),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNININDEX,	"Invalid STUN server/socket index" ),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOBINDRES,	"No STUN binding response in the message" ),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNRECVERRATTR,	"Received STUN error attribute" ),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOMAP,	"No STUN mapped address attribute" ),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOTRESPOND,	"Received no response from STUN server" ),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNSYMMETRIC,	"Symetric NAT detected by STUN" ),
+
+    /* XML errors */
+    PJ_BUILD_ERR( PJLIB_UTIL_EINXML,		"Invalid XML message" ),
+
+    /* DNS errors */
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNSQRYTOOSMALL,	"DNS query packet buffer is too small"),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNSINSIZE,	"Invalid DNS packet length"),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNSINCLASS,	"Invalid DNS class"),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNSINNAMEPTR,	"Invalid DNS name pointer"),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNSINNSADDR,	"Invalid DNS nameserver address"),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNSNONS,		"No nameserver is in DNS resolver"),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNSNOWORKINGNS,	"No working DNS nameserver"),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNSNOANSWERREC,	"No answer record in the DNS response"),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNSINANSWER,	"Invalid DNS answer"),
+
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNS_FORMERR,	"DNS \"Format error\""),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNS_SERVFAIL,	"DNS \"Server failure\""),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNS_NXDOMAIN,	"DNS \"Name Error\""),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNS_NOTIMPL,	"DNS \"Not Implemented\""),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNS_REFUSED,	"DNS \"Refused\""),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNS_YXDOMAIN,	"DNS \"The name exists\""),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNS_YXRRSET,	"DNS \"The RRset (name, type) exists\""),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNS_NXRRSET,	"DNS \"The RRset (name, type) does not exist\""),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNS_NOTAUTH,	"DNS \"Not authorized\""),
+    PJ_BUILD_ERR( PJLIB_UTIL_EDNS_NOTZONE,	"DNS \"The zone specified is not a zone\""),
+
+    /* STUN */
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNTOOMANYATTR,	"Too many STUN attributes"),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNUNKNOWNATTR,	"Unknown STUN attribute"),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNINADDRLEN,	"Invalid STUN socket address length"),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNIPV6NOTSUPP,	"STUN IPv6 attribute not supported"),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOTRESPONSE,	"Expecting STUN response message"),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNINVALIDID,	"STUN transaction ID mismatch"),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOHANDLER,	"Unable to find STUN handler for the request"),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNMSGINTPOS,	"Found non-FINGERPRINT attr. after MESSAGE-INTEGRITY"),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNFINGERPOS,	"Found STUN attribute after FINGERPRINT"),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOUSERNAME,	"Missing STUN USERNAME attribute"),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNMSGINT,	"Missing/invalid STUN MESSAGE-INTEGRITY attribute"),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNDUPATTR,	"Found duplicate STUN attribute"),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOREALM,	"Missing STUN REALM attribute"),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNONCE,	"Missing/stale STUN NONCE attribute value"),
+    PJ_BUILD_ERR( PJLIB_UTIL_ESTUNTSXFAILED,	"STUN transaction terminates with failure"),
+
+    /* HTTP Client */
+    PJ_BUILD_ERR( PJLIB_UTIL_EHTTPINURL,	"Invalid URL format"),
+    PJ_BUILD_ERR( PJLIB_UTIL_EHTTPINPORT,	"Invalid URL port number"),
+    PJ_BUILD_ERR( PJLIB_UTIL_EHTTPINCHDR,	"Incomplete response header received"),
+    PJ_BUILD_ERR( PJLIB_UTIL_EHTTPINSBUF,	"Insufficient buffer"),
+    PJ_BUILD_ERR( PJLIB_UTIL_EHTTPLOST,	        "Connection lost"),
+
+    /* CLI */
+    PJ_BUILD_ERR( PJ_CLI_EEXIT,	                "Exit current session"),
+    PJ_BUILD_ERR( PJ_CLI_EMISSINGARG,	        "Missing argument"),
+    PJ_BUILD_ERR( PJ_CLI_ETOOMANYARGS,	        "Too many arguments"),
+    PJ_BUILD_ERR( PJ_CLI_EINVARG,	        "Invalid argument"),
+    PJ_BUILD_ERR( PJ_CLI_EBADNAME,	        "Command name already exists"),
+    PJ_BUILD_ERR( PJ_CLI_EBADID,	        "Command id already exists"),
+    PJ_BUILD_ERR( PJ_CLI_EBADXML,	        "Invalid XML format"),
+    PJ_BUILD_ERR( PJ_CLI_ETELNETLOST,	        "Connection lost"),
+};
+#endif	/* PJ_HAS_ERROR_STRING */
+
+
+/*
+ * pjlib_util_strerror()
+ */
+pj_str_t pjlib_util_strerror(pj_status_t statcode, 
+			     char *buf, pj_size_t bufsize )
+{
+    pj_str_t errstr;
+
+#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0)
+
+    if (statcode >= PJLIB_UTIL_ERRNO_START && 
+	statcode < PJLIB_UTIL_ERRNO_START + PJ_ERRNO_SPACE_SIZE)
+    {
+	/* Find the error in the table.
+	 * Use binary search!
+	 */
+	int first = 0;
+	int n = PJ_ARRAY_SIZE(err_str);
+
+	while (n > 0) {
+	    int half = n/2;
+	    int mid = first + half;
+
+	    if (err_str[mid].code < statcode) {
+		first = mid+1;
+		n -= (half+1);
+	    } else if (err_str[mid].code > statcode) {
+		n = half;
+	    } else {
+		first = mid;
+		break;
+	    }
+	}
+
+
+	if (PJ_ARRAY_SIZE(err_str) && err_str[first].code == statcode) {
+	    pj_str_t msg;
+	    
+	    msg.ptr = (char*)err_str[first].msg;
+	    msg.slen = pj_ansi_strlen(err_str[first].msg);
+
+	    errstr.ptr = buf;
+	    pj_strncpy_with_null(&errstr, &msg, bufsize);
+	    return errstr;
+
+	} 
+    }
+
+#endif	/* PJ_HAS_ERROR_STRING */
+
+
+    /* Error not found. */
+    errstr.ptr = buf;
+    errstr.slen = pj_ansi_snprintf(buf, bufsize, 
+				   "Unknown pjlib-util error %d",
+				   statcode);
+
+    return errstr;
+}
+
+
+PJ_DEF(pj_status_t) pjlib_util_init(void)
+{
+    pj_status_t status;
+    
+    status = pj_register_strerror(PJLIB_UTIL_ERRNO_START, 
+				  PJ_ERRNO_SPACE_SIZE, 
+				  &pjlib_util_strerror);
+    pj_assert(status == PJ_SUCCESS);
+
+    return PJ_SUCCESS;
+}
diff --git a/jni/pjproject-android/.svn/pristine/91/91aedc72d3369aaa7f6c7f8de2336090bae8030c.svn-base b/jni/pjproject-android/.svn/pristine/91/91aedc72d3369aaa7f6c7f8de2336090bae8030c.svn-base
new file mode 100644
index 0000000..595b647
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/91/91aedc72d3369aaa7f6c7f8de2336090bae8030c.svn-base
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# update.sh
+#
+# update copyright dates in files
+
+a=`find . -name "*.[ch]"`
+for x in $a; do 
+    sed 's/(c) 2001-2005/(c) 2001-2006/' $x > $x.tmp; 
+    mv $x.tmp $x; 
+done
+
+
+
+ 
diff --git a/jni/pjproject-android/.svn/pristine/91/91f3480cae8a4d1598dd0239f94cbf372ead9e71.svn-base b/jni/pjproject-android/.svn/pristine/91/91f3480cae8a4d1598dd0239f94cbf372ead9e71.svn-base
new file mode 100644
index 0000000..3e9dfb5
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/91/91f3480cae8a4d1598dd0239f94cbf372ead9e71.svn-base
@@ -0,0 +1,396 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 
+ */
+#include <pjmedia/vid_tee.h>
+#include <pjmedia/converter.h>
+#include <pjmedia/errno.h>
+#include <pj/array.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+
+
+#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
+
+
+#define TEE_PORT_NAME	"vid_tee"
+#define TEE_PORT_SIGN	PJMEDIA_SIG_PORT_VID_TEE
+
+#define THIS_FILE	"vid_tee.c"
+
+typedef struct vid_tee_dst_port
+{
+    pjmedia_port	*dst;
+    unsigned		 option;
+} vid_tee_dst_port;
+
+
+typedef struct vid_tee_port
+{
+    pjmedia_port	 base;
+    pj_pool_t           *pool;
+    pj_pool_factory     *pf;
+    pj_pool_t           *buf_pool;
+    void		*buf[2];
+    unsigned             buf_cnt;
+    pj_size_t		 buf_size;
+    unsigned		 dst_port_maxcnt;
+    unsigned		 dst_port_cnt;
+    vid_tee_dst_port	*dst_ports;
+    pj_uint8_t		*put_frm_flag;
+    
+    struct vid_tee_conv_t {
+        pjmedia_converter   *conv;
+        pj_size_t            conv_buf_size;        
+    } *tee_conv;
+} vid_tee_port;
+
+
+static pj_status_t tee_put_frame(pjmedia_port *port, pjmedia_frame *frame);
+static pj_status_t tee_get_frame(pjmedia_port *port, pjmedia_frame *frame);
+static pj_status_t tee_destroy(pjmedia_port *port);
+
+/*
+ * Create a video tee port with the specified source media port.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_tee_create( pj_pool_t *pool,
+					    const pjmedia_format *fmt,
+					    unsigned max_dst_cnt,
+					    pjmedia_port **p_vid_tee)
+{
+    vid_tee_port *tee;
+    pj_str_t name_st;
+    const pjmedia_video_format_info *vfi;
+    pjmedia_video_apply_fmt_param vafp;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pool && fmt && p_vid_tee, PJ_EINVAL);
+    PJ_ASSERT_RETURN(fmt->type == PJMEDIA_TYPE_VIDEO, PJ_EINVAL);
+
+    /* Allocate video tee structure */
+    tee = PJ_POOL_ZALLOC_T(pool, vid_tee_port);
+    tee->pf = pool->factory;
+    tee->pool = pj_pool_create(tee->pf, "video tee", 500, 500, NULL);
+
+    /* Initialize video tee structure */
+    tee->dst_port_maxcnt = max_dst_cnt;
+    tee->dst_ports = (vid_tee_dst_port*)
+                     pj_pool_calloc(pool, max_dst_cnt,
+                                    sizeof(vid_tee_dst_port));
+    tee->tee_conv = (struct vid_tee_conv_t *)
+                    pj_pool_calloc(pool, max_dst_cnt,
+                                   sizeof(struct vid_tee_conv_t));
+    tee->put_frm_flag = (pj_uint8_t*)
+			pj_pool_calloc(pool, max_dst_cnt,
+				       sizeof(tee->put_frm_flag[0]));
+
+    /* Initialize video tee buffer, its size is one frame */
+    vfi = pjmedia_get_video_format_info(NULL, fmt->id);
+    if (vfi == NULL)
+	return PJMEDIA_EBADFMT;
+
+    pj_bzero(&vafp, sizeof(vafp));
+    vafp.size = fmt->det.vid.size;
+    status = vfi->apply_fmt(vfi, &vafp);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    tee->buf_size = vafp.framebytes;
+
+    /* Initialize video tee port */
+    status = pjmedia_port_info_init2(&tee->base.info,
+				     pj_strset2(&name_st, (char*)TEE_PORT_NAME),
+				     TEE_PORT_SIGN,
+				     PJMEDIA_DIR_ENCODING,
+				     fmt);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    tee->base.get_frame = &tee_get_frame;
+    tee->base.put_frame = &tee_put_frame;
+    tee->base.on_destroy = &tee_destroy;
+
+    /* Done */
+    *p_vid_tee = &tee->base;
+
+    return PJ_SUCCESS;
+}
+
+static void realloc_buf(vid_tee_port *vid_tee,
+                        unsigned buf_cnt, pj_size_t buf_size)
+{
+    unsigned i;
+    
+    if (buf_cnt > vid_tee->buf_cnt)
+        vid_tee->buf_cnt = buf_cnt;
+    
+    if (buf_size > vid_tee->buf_size) {
+        /* We need a larger buffer here. */
+        vid_tee->buf_size = buf_size;
+        if (vid_tee->buf_pool) {
+            pj_pool_release(vid_tee->buf_pool);
+            vid_tee->buf_pool = NULL;
+        }
+        vid_tee->buf[0] = vid_tee->buf[1] = NULL;
+    }
+    
+    if (!vid_tee->buf_pool) {
+        vid_tee->buf_pool = pj_pool_create(vid_tee->pf, "video tee buffer",
+                                           1000, 1000, NULL);
+    }
+ 
+    for (i = 0; i < vid_tee->buf_cnt; i++) {
+        if (!vid_tee->buf[i])
+            vid_tee->buf[i] = pj_pool_alloc(vid_tee->buf_pool,
+                                            vid_tee->buf_size);
+    }
+}
+
+/*
+ * Add a destination media port to the video tee.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_tee_add_dst_port(pjmedia_port *vid_tee,
+						 unsigned option,
+						 pjmedia_port *port)
+{
+    vid_tee_port *tee = (vid_tee_port*)vid_tee;
+    pjmedia_video_format_detail *vfd;
+
+    PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN,
+		     PJ_EINVAL);
+
+    if (tee->dst_port_cnt >= tee->dst_port_maxcnt)
+	return PJ_ETOOMANY;
+    
+    if (vid_tee->info.fmt.id != port->info.fmt.id)
+	return PJMEDIA_EBADFMT;
+
+    vfd = pjmedia_format_get_video_format_detail(&port->info.fmt, PJ_TRUE);
+    if (vfd->size.w != vid_tee->info.fmt.det.vid.size.w ||
+	vfd->size.h != vid_tee->info.fmt.det.vid.size.h)
+    {
+        return PJMEDIA_EBADFMT;
+    }
+    
+    realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)?
+                1: 0, tee->buf_size);
+
+    pj_bzero(&tee->tee_conv[tee->dst_port_cnt], sizeof(tee->tee_conv[0]));
+    tee->dst_ports[tee->dst_port_cnt].dst = port;
+    tee->dst_ports[tee->dst_port_cnt].option = option;
+    ++tee->dst_port_cnt;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Add a destination media port to the video tee. Create a converter if
+ * necessary.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_tee_add_dst_port2(pjmedia_port *vid_tee,
+						  unsigned option,
+						  pjmedia_port *port)
+{
+    vid_tee_port *tee = (vid_tee_port*)vid_tee;
+    pjmedia_video_format_detail *vfd;
+    
+    PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN,
+		     PJ_EINVAL);
+    
+    if (tee->dst_port_cnt >= tee->dst_port_maxcnt)
+	return PJ_ETOOMANY;
+    
+    pj_bzero(&tee->tee_conv[tee->dst_port_cnt], sizeof(tee->tee_conv[0]));
+    
+    /* Check if we need to create a converter. */
+    vfd = pjmedia_format_get_video_format_detail(&port->info.fmt, PJ_TRUE);
+    if (vid_tee->info.fmt.id != port->info.fmt.id ||
+        vfd->size.w != vid_tee->info.fmt.det.vid.size.w ||
+	vfd->size.h != vid_tee->info.fmt.det.vid.size.h)
+    {
+        const pjmedia_video_format_info *vfi;
+        pjmedia_video_apply_fmt_param vafp;
+        pjmedia_conversion_param conv_param;
+        pj_status_t status;
+
+        vfi = pjmedia_get_video_format_info(NULL, port->info.fmt.id);
+        if (vfi == NULL)
+            return PJMEDIA_EBADFMT;
+
+        pj_bzero(&vafp, sizeof(vafp));
+        vafp.size = port->info.fmt.det.vid.size;
+        status = vfi->apply_fmt(vfi, &vafp);
+        if (status != PJ_SUCCESS)
+            return status;
+        
+        realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)?
+                    2: 1, vafp.framebytes);
+        
+        pjmedia_format_copy(&conv_param.src, &vid_tee->info.fmt);
+	pjmedia_format_copy(&conv_param.dst, &port->info.fmt);
+        
+        status = pjmedia_converter_create(
+                     NULL, tee->pool, &conv_param,
+                     &tee->tee_conv[tee->dst_port_cnt].conv);
+        if (status != PJ_SUCCESS)
+            return status;
+        
+        tee->tee_conv[tee->dst_port_cnt].conv_buf_size = vafp.framebytes;
+    } else {
+        realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)?
+                    1: 0, tee->buf_size);        
+    }
+    
+    tee->dst_ports[tee->dst_port_cnt].dst = port;
+    tee->dst_ports[tee->dst_port_cnt].option = option;
+    ++tee->dst_port_cnt;
+    
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Remove a destination media port from the video tee.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_tee_remove_dst_port(pjmedia_port *vid_tee,
+						    pjmedia_port *port)
+{
+    vid_tee_port *tee = (vid_tee_port*)vid_tee;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN,
+		     PJ_EINVAL);
+
+    for (i = 0; i < tee->dst_port_cnt; ++i) {
+	if (tee->dst_ports[i].dst == port) {
+            if (tee->tee_conv[i].conv)
+                pjmedia_converter_destroy(tee->tee_conv[i].conv);
+            
+	    pj_array_erase(tee->dst_ports, sizeof(tee->dst_ports[0]),
+			   tee->dst_port_cnt, i);
+            pj_array_erase(tee->tee_conv, sizeof(tee->tee_conv[0]),
+			   tee->dst_port_cnt, i);
+	    --tee->dst_port_cnt;
+	    return PJ_SUCCESS;
+	}
+    }
+
+    return PJ_ENOTFOUND;
+}
+
+
+static pj_status_t tee_put_frame(pjmedia_port *port, pjmedia_frame *frame)
+{
+    vid_tee_port *tee = (vid_tee_port*)port;
+    unsigned i, j;
+    const pj_uint8_t PUT_FRM_DONE = 1;
+
+    pj_bzero(tee->put_frm_flag, tee->dst_port_cnt *
+				sizeof(tee->put_frm_flag[0]));
+
+    for (i = 0; i < tee->dst_port_cnt; ++i) {
+	pjmedia_frame frame_ = *frame;
+
+        if (tee->put_frm_flag[i])
+            continue;
+        
+        if (tee->tee_conv[i].conv) {
+            pj_status_t status;
+            
+            frame_.buf  = tee->buf[0];
+            frame_.size = tee->tee_conv[i].conv_buf_size;
+            status = pjmedia_converter_convert(tee->tee_conv[i].conv,
+                                               frame, &frame_);
+            if (status != PJ_SUCCESS) {
+                PJ_LOG(3, (THIS_FILE,
+			       "Failed to convert frame for destination"
+                               " port %d (%.*s)", i,
+                               tee->dst_ports[i].dst->info.name.slen,
+                               tee->dst_ports[i].dst->info.name.ptr));
+                continue;
+            }
+        }
+        
+        /* Find other destination ports which has the same format so
+         * we don't need to do the same conversion twice.
+         */
+        for (j = i; j < tee->dst_port_cnt; ++j) {
+            pjmedia_frame framep;
+            
+            if (tee->put_frm_flag[j] ||
+                (tee->dst_ports[j].dst->info.fmt.id != 
+                 tee->dst_ports[i].dst->info.fmt.id) ||
+                (tee->dst_ports[j].dst->info.fmt.det.vid.size.w != 
+                 tee->dst_ports[i].dst->info.fmt.det.vid.size.w) ||
+                (tee->dst_ports[j].dst->info.fmt.det.vid.size.h != 
+                 tee->dst_ports[i].dst->info.fmt.det.vid.size.h))
+            {
+                continue;
+            }
+            
+            framep = frame_;
+            /* For dst_ports that do in-place processing, we need to duplicate
+             * the data source first.
+             */
+            if (tee->dst_ports[j].option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)
+            {
+                PJ_ASSERT_RETURN(tee->buf_size <= frame_.size, PJ_ETOOBIG);
+                framep.buf = tee->buf[tee->buf_cnt-1];
+                framep.size = frame_.size;
+                pj_memcpy(framep.buf, frame_.buf, frame_.size);
+            }
+
+            /* Deliver the data */
+            pjmedia_port_put_frame(tee->dst_ports[j].dst, &framep);
+            tee->put_frm_flag[j] = PUT_FRM_DONE;
+            
+            if (!tee->tee_conv[i].conv)
+                break;
+        }
+    }
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t tee_get_frame(pjmedia_port *port, pjmedia_frame *frame)
+{
+    PJ_UNUSED_ARG(port);
+    PJ_UNUSED_ARG(frame);
+
+    pj_assert(!"Bug! Tee port get_frame() shouldn't be called.");
+
+    return PJ_EBUG;
+}
+
+static pj_status_t tee_destroy(pjmedia_port *port)
+{
+    vid_tee_port *tee = (vid_tee_port*)port;
+
+    PJ_ASSERT_RETURN(port && port->info.signature==TEE_PORT_SIGN, PJ_EINVAL);
+
+    pj_pool_release(tee->pool);
+    if (tee->buf_pool)
+        pj_pool_release(tee->buf_pool);
+                    
+    pj_bzero(tee, sizeof(*tee));
+
+    return PJ_SUCCESS;
+}
+
+
+#endif /* PJMEDIA_HAS_VIDEO */
diff --git a/jni/pjproject-android/.svn/pristine/91/91f5b2faa024bbd90f27b11bcc65c0232f15b66d.svn-base b/jni/pjproject-android/.svn/pristine/91/91f5b2faa024bbd90f27b11bcc65c0232f15b66d.svn-base
new file mode 100644
index 0000000..42b2b88
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/91/91f5b2faa024bbd90f27b11bcc65c0232f15b66d.svn-base
@@ -0,0 +1,8 @@
+# $Id$
+#
+import inc_const as const
+
+PJSUA = ["--null-audio --extra-audio --max-calls=1 $SIPP_URI"]
+
+# Send hold after remote holds (double hold)
+PJSUA_EXPECTS = [[0, const.MEDIA_HOLD, "H"]]