* #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/pjnath/src/pjnath-test/server.c b/jni/pjproject-android/pjnath/src/pjnath-test/server.c
new file mode 100644
index 0000000..c9e63a3
--- /dev/null
+++ b/jni/pjproject-android/pjnath/src/pjnath-test/server.c
@@ -0,0 +1,754 @@
+/* $Id: server.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * 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 "server.h"
+#include "test.h"
+
+#define THIS_FILE	"server.c"
+#define MAX_STUN_PKT	1500
+#define TURN_NONCE	"thenonce"
+
+static pj_bool_t stun_on_data_recvfrom(pj_activesock_t *asock,
+				       void *data,
+				       pj_size_t size,
+				       const pj_sockaddr_t *src_addr,
+				       int addr_len,
+				       pj_status_t status);
+static pj_bool_t turn_on_data_recvfrom(pj_activesock_t *asock,
+				       void *data,
+				       pj_size_t size,
+				       const pj_sockaddr_t *src_addr,
+				       int addr_len,
+				       pj_status_t status);
+static pj_bool_t alloc_on_data_recvfrom(pj_activesock_t *asock,
+				       void *data,
+				       pj_size_t size,
+				       const pj_sockaddr_t *src_addr,
+				       int addr_len,
+				       pj_status_t status);
+
+pj_status_t create_test_server(pj_stun_config *stun_cfg,
+			       pj_uint32_t flags,
+			       const char *domain,
+			       test_server **p_test_srv)
+{
+    pj_pool_t *pool;
+    test_server *test_srv;
+    pj_sockaddr hostip;
+    char strbuf[100];
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(stun_cfg && domain && p_test_srv, PJ_EINVAL);
+
+    status = pj_gethostip(pj_AF_INET(), &hostip);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pool = pj_pool_create(mem, THIS_FILE, 512, 512, NULL);
+    test_srv = (test_server*) PJ_POOL_ZALLOC_T(pool, test_server);
+    test_srv->pool = pool;
+    test_srv->flags = flags;
+    test_srv->stun_cfg = stun_cfg;
+
+    pj_strdup2(pool, &test_srv->domain, domain);
+    test_srv->username = pj_str(TURN_USERNAME);
+    test_srv->passwd = pj_str(TURN_PASSWD);
+
+    pj_ioqueue_op_key_init(&test_srv->send_key, sizeof(test_srv->send_key));
+
+    if (flags & CREATE_DNS_SERVER) {
+	status = pj_dns_server_create(mem, test_srv->stun_cfg->ioqueue,
+				      pj_AF_INET(), DNS_SERVER_PORT,
+				      0, &test_srv->dns_server);
+	if (status != PJ_SUCCESS) {
+	    destroy_test_server(test_srv);
+	    return status;
+	}
+
+	/* Add DNS A record for the domain, for fallback */
+	if (flags & CREATE_A_RECORD_FOR_DOMAIN) {
+	    pj_dns_parsed_rr rr;
+	    pj_str_t res_name;
+	    pj_in_addr ip_addr;
+
+	    pj_strdup2(pool, &res_name, domain);
+	    ip_addr = hostip.ipv4.sin_addr;
+	    pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr);
+	    pj_dns_server_add_rec(test_srv->dns_server, 1, &rr);
+	}
+
+    }
+
+    if (flags & CREATE_STUN_SERVER) {
+	pj_activesock_cb stun_sock_cb;
+	pj_sockaddr bound_addr;
+
+	pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb));
+	stun_sock_cb.on_data_recvfrom = &stun_on_data_recvfrom;
+
+	pj_sockaddr_in_init(&bound_addr.ipv4, NULL, STUN_SERVER_PORT);
+
+	status = pj_activesock_create_udp(pool, &bound_addr, NULL, 
+					  test_srv->stun_cfg->ioqueue,
+					  &stun_sock_cb, test_srv, 
+					  &test_srv->stun_sock, NULL);
+	if (status != PJ_SUCCESS) {
+	    destroy_test_server(test_srv);
+	    return status;
+	}
+
+	status = pj_activesock_start_recvfrom(test_srv->stun_sock, pool,
+					      MAX_STUN_PKT, 0);
+	if (status != PJ_SUCCESS) {
+	    destroy_test_server(test_srv);
+	    return status;
+	}
+
+	if (test_srv->dns_server && (flags & CREATE_STUN_SERVER_DNS_SRV)) {
+	    pj_str_t res_name, target;
+	    pj_dns_parsed_rr rr;
+	    pj_in_addr ip_addr;
+
+	    /* Add DNS entries:
+	     *  _stun._udp.domain 60 IN SRV 0 0 PORT stun.domain.
+	     *  stun.domain IN A 127.0.0.1
+	     */
+	    pj_ansi_snprintf(strbuf, sizeof(strbuf),
+			     "_stun._udp.%s", domain);
+	    pj_strdup2(pool, &res_name, strbuf);
+	    pj_ansi_snprintf(strbuf, sizeof(strbuf),
+			     "stun.%s", domain);
+	    pj_strdup2(pool, &target, strbuf);
+	    pj_dns_init_srv_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, 0, 0, 
+			       STUN_SERVER_PORT, &target);
+	    pj_dns_server_add_rec(test_srv->dns_server, 1, &rr);
+
+	    res_name = target;
+	    ip_addr = hostip.ipv4.sin_addr;
+	    pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr);
+	    pj_dns_server_add_rec(test_srv->dns_server, 1, &rr);
+	}
+
+    }
+
+    if (flags & CREATE_TURN_SERVER) {
+	pj_activesock_cb turn_sock_cb;
+	pj_sockaddr bound_addr;
+
+	pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb));
+	turn_sock_cb.on_data_recvfrom = &turn_on_data_recvfrom;
+
+	pj_sockaddr_in_init(&bound_addr.ipv4, NULL, TURN_SERVER_PORT);
+
+	status = pj_activesock_create_udp(pool, &bound_addr, NULL, 
+					  test_srv->stun_cfg->ioqueue,
+					  &turn_sock_cb, test_srv,
+					  &test_srv->turn_sock, NULL);
+	if (status != PJ_SUCCESS) {
+	    destroy_test_server(test_srv);
+	    return status;
+	}
+
+	status = pj_activesock_start_recvfrom(test_srv->turn_sock, pool,
+					      MAX_STUN_PKT, 0);
+	if (status != PJ_SUCCESS) {
+	    destroy_test_server(test_srv);
+	    return status;
+	}
+
+	if (test_srv->dns_server && (flags & CREATE_TURN_SERVER_DNS_SRV)) {
+	    pj_str_t res_name, target;
+	    pj_dns_parsed_rr rr;
+	    pj_in_addr ip_addr;
+
+	    /* Add DNS entries:
+	     *  _turn._udp.domain 60 IN SRV 0 0 PORT turn.domain.
+	     *  turn.domain IN A 127.0.0.1
+	     */
+	    pj_ansi_snprintf(strbuf, sizeof(strbuf),
+			     "_turn._udp.%s", domain);
+	    pj_strdup2(pool, &res_name, strbuf);
+	    pj_ansi_snprintf(strbuf, sizeof(strbuf),
+			     "turn.%s", domain);
+	    pj_strdup2(pool, &target, strbuf);
+	    pj_dns_init_srv_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, 0, 0, 
+			       TURN_SERVER_PORT, &target);
+	    pj_dns_server_add_rec(test_srv->dns_server, 1, &rr);
+
+	    res_name = target;
+	    ip_addr = hostip.ipv4.sin_addr;
+	    pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr);
+	    pj_dns_server_add_rec(test_srv->dns_server, 1, &rr);
+	}
+    }
+
+    *p_test_srv = test_srv;
+    return PJ_SUCCESS;
+}
+
+void destroy_test_server(test_server *test_srv)
+{
+    unsigned i;
+
+    PJ_ASSERT_ON_FAIL(test_srv, return);
+
+    for (i=0; i<test_srv->turn_alloc_cnt; ++i) {
+	pj_activesock_close(test_srv->turn_alloc[i].sock);
+	pj_pool_release(test_srv->turn_alloc[i].pool);
+    }
+    test_srv->turn_alloc_cnt = 0;
+
+    if (test_srv->turn_sock) {
+	pj_activesock_close(test_srv->turn_sock);
+	test_srv->turn_sock = NULL;
+    }
+
+    if (test_srv->stun_sock) {
+	pj_activesock_close(test_srv->stun_sock);
+	test_srv->stun_sock = NULL;
+    }
+
+    if (test_srv->dns_server) {
+	pj_dns_server_destroy(test_srv->dns_server);
+	test_srv->dns_server = NULL;
+    }
+
+    if (test_srv->pool) {
+	pj_pool_t *pool = test_srv->pool;
+	test_srv->pool = NULL;
+	pj_pool_release(pool);
+    }
+}
+
+static pj_bool_t stun_on_data_recvfrom(pj_activesock_t *asock,
+				       void *data,
+				       pj_size_t size,
+				       const pj_sockaddr_t *src_addr,
+				       int addr_len,
+				       pj_status_t status)
+{
+    test_server *test_srv;
+    pj_stun_msg *req, *resp = NULL;
+    pj_pool_t *pool;
+    pj_ssize_t len;
+
+    if (status != PJ_SUCCESS)
+	return PJ_TRUE;
+
+    test_srv = (test_server*) pj_activesock_get_user_data(asock);
+    pool = pj_pool_create(test_srv->stun_cfg->pf, NULL, 512, 512, NULL);
+
+    status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size, 
+				PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, 
+				&req, NULL, NULL);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    if (req->hdr.type != PJ_STUN_BINDING_REQUEST) {
+	pj_stun_msg_create_response(pool, req, PJ_STUN_SC_BAD_REQUEST, 
+				    NULL, &resp);
+	goto send_pkt;
+    }
+
+    status = pj_stun_msg_create_response(pool, req, 0, NULL, &resp);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_XOR_MAPPED_ADDR,
+				  PJ_TRUE, src_addr, addr_len);
+
+send_pkt:
+    status = pj_stun_msg_encode(resp, (pj_uint8_t*)data, MAX_STUN_PKT, 
+				0, NULL, &size);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    len = size;
+    status = pj_activesock_sendto(asock, &test_srv->send_key, data, &len,
+				  0, src_addr, addr_len);
+
+on_return:
+    pj_pool_release(pool);
+    return PJ_TRUE;
+}
+
+
+static pj_stun_msg* create_success_response(test_server *test_srv,
+					    turn_allocation *alloc,
+					    pj_stun_msg *req,
+					    pj_pool_t *pool,
+					    unsigned lifetime,
+					    pj_str_t *auth_key)
+{
+    pj_stun_msg *resp;
+    pj_str_t tmp;
+    pj_status_t status;
+
+    /* Create response */
+    status = pj_stun_msg_create_response(pool, req, 0, NULL, &resp);
+    if (status != PJ_SUCCESS) {
+	return NULL;
+    }
+    /* Add TURN_NONCE */
+    pj_stun_msg_add_string_attr(pool, resp, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, TURN_NONCE));
+    /* Add LIFETIME */
+    pj_stun_msg_add_uint_attr(pool, resp, PJ_STUN_ATTR_LIFETIME, lifetime);
+    if (lifetime != 0) {
+	/* Add XOR-RELAYED-ADDRESS */
+	pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_XOR_RELAYED_ADDR, PJ_TRUE, &alloc->alloc_addr,
+				      pj_sockaddr_get_len(&alloc->alloc_addr));
+	/* Add XOR-MAPPED-ADDRESS */
+	pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &alloc->client_addr,
+				      pj_sockaddr_get_len(&alloc->client_addr));
+    }
+
+    /* Add blank MESSAGE-INTEGRITY */
+    pj_stun_msg_add_msgint_attr(pool, resp);
+
+    /* Set auth key */
+    pj_stun_create_key(pool, auth_key, &test_srv->domain, &test_srv->username,
+		       PJ_STUN_PASSWD_PLAIN, &test_srv->passwd);
+
+    return resp;
+}
+
+
+static pj_bool_t turn_on_data_recvfrom(pj_activesock_t *asock,
+				       void *data,
+				       pj_size_t size,
+				       const pj_sockaddr_t *src_addr,
+				       int addr_len,
+				       pj_status_t status)
+{
+    test_server *test_srv;
+    pj_pool_t *pool;
+    turn_allocation *alloc;
+    pj_stun_msg *req, *resp = NULL;
+    pj_str_t auth_key = { NULL, 0 };
+    char client_info[PJ_INET6_ADDRSTRLEN+10];
+    unsigned i;
+    pj_ssize_t len;
+
+    if (status != PJ_SUCCESS)
+	return PJ_TRUE;
+
+    pj_sockaddr_print(src_addr, client_info, sizeof(client_info), 3);
+
+    test_srv = (test_server*) pj_activesock_get_user_data(asock);
+    pool = pj_pool_create(test_srv->stun_cfg->pf, NULL, 512, 512, NULL);
+
+    /* Find the client */
+    for (i=0; i<test_srv->turn_alloc_cnt; i++) {
+	if (pj_sockaddr_cmp(&test_srv->turn_alloc[i].client_addr, src_addr)==0)
+	    break;
+    }
+
+
+    if (pj_stun_msg_check((pj_uint8_t*)data, size, PJ_STUN_NO_FINGERPRINT_CHECK)!=PJ_SUCCESS)  {
+	/* Not STUN message, this probably is a ChannelData */
+	pj_turn_channel_data cd;
+	const pj_turn_channel_data *pcd = (const pj_turn_channel_data*)data;
+	pj_ssize_t sent;
+
+	if (i==test_srv->turn_alloc_cnt) {
+	    /* Invalid data */
+	    PJ_LOG(1,(THIS_FILE, 
+		      "TURN Server received strayed data"));
+	    goto on_return;
+	}
+
+	alloc = &test_srv->turn_alloc[i];
+
+	cd.ch_number = pj_ntohs(pcd->ch_number);
+	cd.length = pj_ntohs(pcd->length);
+
+	/* For UDP check the packet length */
+	if (size < cd.length+sizeof(cd)) {
+	    PJ_LOG(1,(THIS_FILE, 
+		      "TURN Server: ChannelData discarded: UDP size error"));
+	    goto on_return;
+	}
+
+	/* Lookup peer */
+	for (i=0; i<alloc->perm_cnt; ++i) {
+	    if (alloc->chnum[i] == cd.ch_number)
+		break;
+	}
+
+	if (i==alloc->perm_cnt) {
+	    PJ_LOG(1,(THIS_FILE, 
+		      "TURN Server: ChannelData discarded: invalid channel number"));
+	    goto on_return;
+	}
+
+	/* Relay the data to peer */
+	sent = cd.length;
+	pj_activesock_sendto(alloc->sock, &alloc->send_key,
+			     pcd+1, &sent, 0,
+			     &alloc->perm[i],
+			     pj_sockaddr_get_len(&alloc->perm[i]));
+
+	/* Done */
+	goto on_return;
+    }
+
+    status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size, 
+				PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET |
+				    PJ_STUN_NO_FINGERPRINT_CHECK, 
+				&req, NULL, NULL);
+    if (status != PJ_SUCCESS) {
+	char errmsg[PJ_ERR_MSG_SIZE];
+	pj_strerror(status, errmsg, sizeof(errmsg));
+	PJ_LOG(1,("", "STUN message decode error from client %s: %s", client_info, errmsg));
+	goto on_return;
+    }
+
+    if (i==test_srv->turn_alloc_cnt) {
+	/* New client */
+	//pj_str_t ip_addr;
+	pj_stun_username_attr *uname;
+	pj_activesock_cb alloc_sock_cb;
+	turn_allocation *alloc;
+
+	/* Must be Allocate request */
+	if (req->hdr.type != PJ_STUN_ALLOCATE_REQUEST) {
+	    PJ_LOG(1,(THIS_FILE, "Invalid %s %s from client %s",
+		      pj_stun_get_method_name(req->hdr.type),
+		      pj_stun_get_class_name(req->hdr.type),
+		      client_info));
+
+	    if (PJ_STUN_IS_REQUEST(req->hdr.type))
+		pj_stun_msg_create_response(pool, req, PJ_STUN_SC_BAD_REQUEST, NULL, &resp);
+	    goto send_pkt;
+	}
+
+	test_srv->turn_stat.rx_allocate_cnt++;
+
+	/* Skip if we're not responding to Allocate request */
+	if (!test_srv->turn_respond_allocate)
+	    return PJ_TRUE;
+
+	/* Check if we have too many clients */
+	if (test_srv->turn_alloc_cnt == MAX_TURN_ALLOC) {
+	    pj_stun_msg_create_response(pool, req, PJ_STUN_SC_INSUFFICIENT_CAPACITY, NULL, &resp);
+	    goto send_pkt;
+	}
+
+	/* Get USERNAME attribute */
+	uname = (pj_stun_username_attr*)
+		pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0);
+
+	/* Reject if it doesn't have MESSAGE-INTEGRITY or USERNAME attributes or
+	 * the user is incorrect
+	 */
+	if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0) == NULL ||
+	    uname==NULL || pj_stricmp2(&uname->value, TURN_USERNAME) != 0) 
+	{
+	    pj_str_t tmp;
+
+	    pj_stun_msg_create_response(pool, req, PJ_STUN_SC_UNAUTHORIZED, NULL, &resp);
+	    pj_stun_msg_add_string_attr(pool, resp, PJ_STUN_ATTR_REALM, &test_srv->domain);
+	    pj_stun_msg_add_string_attr(pool, resp, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, TURN_NONCE));
+	    goto send_pkt;
+	}
+
+	pj_bzero(&alloc_sock_cb, sizeof(alloc_sock_cb));
+	alloc_sock_cb.on_data_recvfrom = &alloc_on_data_recvfrom;
+
+	/* Create allocation */
+	alloc = &test_srv->turn_alloc[test_srv->turn_alloc_cnt];
+	alloc->perm_cnt = 0;
+	alloc->test_srv = test_srv;
+	pj_memcpy(&alloc->client_addr, src_addr, addr_len);
+	pj_ioqueue_op_key_init(&alloc->send_key, sizeof(alloc->send_key));
+
+	alloc->pool = pj_pool_create(test_srv->stun_cfg->pf, "alloc", 512, 512, NULL);
+
+	/* Create relay socket */
+	pj_sockaddr_in_init(&alloc->alloc_addr.ipv4, NULL, 0);
+	pj_gethostip(pj_AF_INET(), &alloc->alloc_addr);
+
+	status = pj_activesock_create_udp(alloc->pool, &alloc->alloc_addr, NULL, 
+					  test_srv->stun_cfg->ioqueue,
+					  &alloc_sock_cb, alloc, 
+					  &alloc->sock, &alloc->alloc_addr);
+	if (status != PJ_SUCCESS) {
+	    pj_pool_release(alloc->pool);
+	    pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp);
+	    goto send_pkt;
+	}
+	//pj_sockaddr_set_str_addr(pj_AF_INET(), &alloc->alloc_addr, &ip_addr);
+
+	pj_activesock_set_user_data(alloc->sock, alloc);
+
+	status = pj_activesock_start_recvfrom(alloc->sock, alloc->pool, 1500, 0);
+	if (status != PJ_SUCCESS) {
+	    pj_activesock_close(alloc->sock);
+	    pj_pool_release(alloc->pool);
+	    pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp);
+	    goto send_pkt;
+	}
+
+	/* Create Data indication */
+	status = pj_stun_msg_create(alloc->pool, PJ_STUN_DATA_INDICATION,
+				    PJ_STUN_MAGIC, NULL, &alloc->data_ind);
+	if (status != PJ_SUCCESS) {
+	    pj_activesock_close(alloc->sock);
+	    pj_pool_release(alloc->pool);
+	    pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp);
+	    goto send_pkt;
+	}
+	pj_stun_msg_add_sockaddr_attr(alloc->pool, alloc->data_ind, 
+				      PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE,
+				      &alloc->alloc_addr,
+				      pj_sockaddr_get_len(&alloc->alloc_addr));
+	pj_stun_msg_add_binary_attr(alloc->pool, alloc->data_ind,
+				    PJ_STUN_ATTR_DATA, (pj_uint8_t*)"", 1);
+
+	/* Create response */
+	resp = create_success_response(test_srv, alloc, req, pool, 600, &auth_key);
+	if (resp == NULL) {
+	    pj_activesock_close(alloc->sock);
+	    pj_pool_release(alloc->pool);
+	    pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp);
+	    goto send_pkt;
+	}
+
+	++test_srv->turn_alloc_cnt;
+
+    } else {
+	alloc = &test_srv->turn_alloc[i];
+
+	if (req->hdr.type == PJ_STUN_ALLOCATE_REQUEST) {
+
+	    test_srv->turn_stat.rx_allocate_cnt++;
+
+	    /* Skip if we're not responding to Allocate request */
+	    if (!test_srv->turn_respond_allocate)
+		return PJ_TRUE;
+
+	    resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key);
+
+	} else if (req->hdr.type == PJ_STUN_REFRESH_REQUEST) {
+	    pj_stun_lifetime_attr *lf_attr;
+
+	    test_srv->turn_stat.rx_refresh_cnt++;
+
+	    /* Skip if we're not responding to Refresh request */
+	    if (!test_srv->turn_respond_refresh)
+		return PJ_TRUE;
+
+	    lf_attr = (pj_stun_lifetime_attr*)
+		      pj_stun_msg_find_attr(req, PJ_STUN_ATTR_LIFETIME, 0);
+	    if (lf_attr && lf_attr->value != 0) {
+		resp = create_success_response(test_srv, alloc, req, pool, 600, &auth_key);
+		pj_array_erase(test_srv->turn_alloc, sizeof(test_srv->turn_alloc[0]),
+			       test_srv->turn_alloc_cnt, i);
+		--test_srv->turn_alloc_cnt;
+	    } else
+		resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key);
+	} else if (req->hdr.type == PJ_STUN_CREATE_PERM_REQUEST) {
+	    for (i=0; i<req->attr_count; ++i) {
+		if (req->attr[i]->type == PJ_STUN_ATTR_XOR_PEER_ADDR) {
+		    pj_stun_xor_peer_addr_attr *pa = (pj_stun_xor_peer_addr_attr*)req->attr[i];
+		    unsigned j;
+
+		    for (j=0; j<alloc->perm_cnt; ++j) {
+			if (pj_sockaddr_cmp(&alloc->perm[j], &pa->sockaddr)==0)
+			    break;
+		    }
+
+		    if (j==alloc->perm_cnt && alloc->perm_cnt < MAX_TURN_PERM) {
+			char peer_info[PJ_INET6_ADDRSTRLEN];
+			pj_sockaddr_print(&pa->sockaddr, peer_info, sizeof(peer_info), 3);
+
+			pj_sockaddr_cp(&alloc->perm[alloc->perm_cnt], &pa->sockaddr);
+			++alloc->perm_cnt;
+
+			PJ_LOG(5,("", "Permission %s added to client %s, perm_cnt=%d", 
+				      peer_info, client_info, alloc->perm_cnt));
+		    }
+
+		}
+	    }
+	    resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key);
+	} else if (req->hdr.type == PJ_STUN_SEND_INDICATION) {
+	    pj_stun_xor_peer_addr_attr *pa;
+	    pj_stun_data_attr *da;
+
+	    test_srv->turn_stat.rx_send_ind_cnt++;
+
+	    pa = (pj_stun_xor_peer_addr_attr*)
+		 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_XOR_PEER_ADDR, 0);
+	    da = (pj_stun_data_attr*)
+		 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_DATA, 0);
+	    if (pa && da) {
+		unsigned j;
+		char peer_info[PJ_INET6_ADDRSTRLEN];
+		pj_ssize_t sent;
+
+		pj_sockaddr_print(&pa->sockaddr, peer_info, sizeof(peer_info), 3);
+
+		for (j=0; j<alloc->perm_cnt; ++j) {
+		    if (pj_sockaddr_cmp(&alloc->perm[j], &pa->sockaddr)==0)
+			break;
+		}
+
+		if (j==alloc->perm_cnt) {
+		    PJ_LOG(5,("", "SendIndication to %s is rejected (no permission)", 
+			          peer_info, client_info, alloc->perm_cnt));
+		} else {
+		    PJ_LOG(5,(THIS_FILE, "Relaying %d bytes data from client %s to peer %s, "
+					 "perm_cnt=%d", 
+			      da->length, client_info, peer_info, alloc->perm_cnt));
+
+		    sent = da->length;
+		    pj_activesock_sendto(alloc->sock, &alloc->send_key,
+					 da->data, &sent, 0,
+					 &pa->sockaddr,
+					 pj_sockaddr_get_len(&pa->sockaddr));
+		}
+	    } else {
+		PJ_LOG(1,(THIS_FILE, "Invalid Send Indication from %s", client_info));
+	    }
+	} else if (req->hdr.type == PJ_STUN_CHANNEL_BIND_REQUEST) {
+	    pj_stun_xor_peer_addr_attr *pa;
+	    pj_stun_channel_number_attr *cna;
+	    unsigned j, cn;
+
+	    pa = (pj_stun_xor_peer_addr_attr*)
+		 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_XOR_PEER_ADDR, 0);
+	    cna = (pj_stun_channel_number_attr*)
+		 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_CHANNEL_NUMBER, 0);
+	    cn = PJ_STUN_GET_CH_NB(cna->value);
+
+	    resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key);
+
+	    for (j=0; j<alloc->perm_cnt; ++j) {
+		if (pj_sockaddr_cmp(&alloc->perm[j], &pa->sockaddr)==0)
+		    break;
+	    }
+
+	    if (i==alloc->perm_cnt) {
+		if (alloc->perm_cnt==MAX_TURN_PERM) {
+		    pj_stun_msg_create_response(pool, req, PJ_STUN_SC_INSUFFICIENT_CAPACITY, NULL, &resp);
+		    goto send_pkt;
+		}
+		pj_sockaddr_cp(&alloc->perm[i], &pa->sockaddr);
+		++alloc->perm_cnt;
+	    }
+	    alloc->chnum[i] = cn;
+
+	    resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key);
+
+	} else if (PJ_STUN_IS_REQUEST(req->hdr.type)) {
+	    pj_stun_msg_create_response(pool, req, PJ_STUN_SC_BAD_REQUEST, NULL, &resp);
+	}
+    }
+
+
+send_pkt:
+    if (resp) {
+	status = pj_stun_msg_encode(resp, (pj_uint8_t*)data, MAX_STUN_PKT, 
+				    0, &auth_key, &size);
+	if (status != PJ_SUCCESS)
+	    goto on_return;
+
+	len = size;
+	status = pj_activesock_sendto(asock, &test_srv->send_key, data, &len,
+				      0, src_addr, addr_len);
+    }
+
+on_return:
+    pj_pool_release(pool);
+    return PJ_TRUE;
+}
+
+/* On received data from peer */
+static pj_bool_t alloc_on_data_recvfrom(pj_activesock_t *asock,
+				       void *data,
+				       pj_size_t size,
+				       const pj_sockaddr_t *src_addr,
+				       int addr_len,
+				       pj_status_t status)
+{
+    turn_allocation *alloc;
+    pj_stun_xor_peer_addr_attr *pa;
+    pj_stun_data_attr *da;
+    char peer_info[PJ_INET6_ADDRSTRLEN+10];
+    char client_info[PJ_INET6_ADDRSTRLEN+10];
+    pj_uint8_t buffer[1500];
+    pj_ssize_t sent;
+    unsigned i;
+
+    if (status != PJ_SUCCESS)
+	return PJ_TRUE;
+
+    alloc = (turn_allocation*) pj_activesock_get_user_data(asock);
+
+    pj_sockaddr_print(&alloc->client_addr, client_info, sizeof(client_info), 3);
+    pj_sockaddr_print(src_addr, peer_info, sizeof(peer_info), 3);
+
+    /* Check that this peer has a permission */
+    for (i=0; i<alloc->perm_cnt; ++i) {
+	if (pj_sockaddr_get_len(&alloc->perm[i]) == (unsigned)addr_len &&
+	    pj_memcmp(pj_sockaddr_get_addr(&alloc->perm[i]),
+		      pj_sockaddr_get_addr(src_addr),
+		      addr_len) == 0)
+	{
+	    break;
+	}
+    }
+    if (i==alloc->perm_cnt) {
+	PJ_LOG(5,("", "Client %s received %d bytes unauthorized data from peer %s", 
+		      client_info, size, peer_info));
+	if (alloc->perm_cnt == 0)
+	    PJ_LOG(5,("", "Client %s has no permission", client_info));
+	return PJ_TRUE;
+    }
+
+    /* Format a Data indication */
+    pa = (pj_stun_xor_peer_addr_attr*)
+	 pj_stun_msg_find_attr(alloc->data_ind, PJ_STUN_ATTR_XOR_PEER_ADDR, 0);
+    da = (pj_stun_data_attr*)
+	 pj_stun_msg_find_attr(alloc->data_ind, PJ_STUN_ATTR_DATA, 0);
+    pj_assert(pa && da);
+
+    pj_sockaddr_cp(&pa->sockaddr, src_addr);
+    da->data = (pj_uint8_t*)data;
+    da->length = (unsigned)size;
+
+    /* Encode Data indication */
+    status = pj_stun_msg_encode(alloc->data_ind, buffer, sizeof(buffer), 0,
+				NULL, &size);
+    if (status != PJ_SUCCESS)
+	return PJ_TRUE;
+
+    /* Send */
+    sent = size;
+    PJ_LOG(5,("", "Forwarding %d bytes data from peer %s to client %s", 
+		   sent, peer_info, client_info));
+
+    pj_activesock_sendto(alloc->test_srv->turn_sock, &alloc->send_key, buffer,
+			 &sent, 0, &alloc->client_addr,
+			 pj_sockaddr_get_len(&alloc->client_addr));
+
+    return PJ_TRUE;
+}
+