Continuing work on the new STUN framework, partly implemented the client session

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@993 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjlib-util/src/pjstun-client/client_main.c b/pjlib-util/src/pjstun-client/client_main.c
new file mode 100644
index 0000000..7dc0540
--- /dev/null
+++ b/pjlib-util/src/pjstun-client/client_main.c
@@ -0,0 +1,25 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2005 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.h>
+#include <pjlib.h>
+
+
+#define THIS_FILE	"client_main.c"
+#define MAX_THREADS	8
+
diff --git a/pjlib-util/src/pjstun-client/stun_session.c b/pjlib-util/src/pjstun-client/stun_session.c
new file mode 100644
index 0000000..571b723
--- /dev/null
+++ b/pjlib-util/src/pjstun-client/stun_session.c
@@ -0,0 +1,499 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2005 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 "stun_session.h"
+#include <pjlib.h>
+
+struct pj_stun_session
+{
+    pj_stun_endpoint	*endpt;
+    pj_pool_t		*pool;
+    pj_stun_session_cb	 cb;
+    void		*user_data;
+
+    pj_str_t		 realm;
+    pj_str_t		 username;
+    pj_str_t		 password;
+
+    pj_bool_t		 fingerprint_enabled;
+
+    pj_stun_tx_data	 pending_request_list;
+};
+
+#define SNAME(s_)		    ((s_)->pool->obj_name)
+
+#if PJ_LOG_MAX_LEVEL >= 5
+#   define TRACE_(expr)		    PJ_LOG(5,expr)
+#else
+#   define TRACE_(expr)
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 4
+#   define LOG_ERR_(sess, title, rc)
+static void stun_perror(pj_stun_session *sess, const char *title, 
+			pj_status_t status)
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+
+    pj_strerror(status, errmsg, sizeof(errmsg));
+
+    PJ_LOG(4,(SNAME(sess), "%s: %s", title, errmsg));
+}
+
+#else
+#   define ERR_(sess, title, rc)
+#endif
+
+#define TDATA_POOL_SIZE		    1024
+#define TDATA_POOL_INC		    1024
+
+
+static void tsx_on_complete(pj_stun_client_tsx *tsx,
+			       pj_status_t status, 
+			       const pj_stun_msg *response);
+static pj_status_t tsx_on_send_msg(pj_stun_client_tsx *tsx,
+				   const void *stun_pkt,
+				   pj_size_t pkt_size);
+
+static pj_stun_tsx_cb tsx_cb = 
+{
+    &tsx_on_complete,
+    &tsx_on_send_msg
+};
+
+
+static pj_status_t tsx_add(pj_stun_session *sess,
+			   pj_stun_tx_data *tdata)
+{
+    pj_list_push_back(&sess->pending_request_list, tdata);
+    return PJ_SUCCESS;
+}
+
+static pj_status_t tsx_erase(pj_stun_session *sess,
+			     pj_stun_tx_data *tdata)
+{
+    pj_list_erase(tdata);
+    return PJ_SUCCESS;
+}
+
+static pj_stun_tx_data* tsx_lookup(pj_stun_session *sess,
+				   const pj_stun_msg *msg)
+{
+    pj_stun_tx_data *tdata;
+
+    tdata = sess->pending_request_list.next;
+    while (tdata != &sess->pending_request_list) {
+	pj_assert(sizeof(tdata->client_key)==sizeof(msg->hdr.tsx_id));
+	if (pj_memcmp(tdata->client_key, msg->hdr.tsx_id, 
+		      sizeof(msg->hdr.tsx_id))==0)
+	{
+	    return tdata;
+	}
+	tdata = tdata->next;
+    }
+
+    return NULL;
+}
+
+static pj_status_t create_tdata(pj_stun_session *sess,
+				unsigned msg_type,
+				void *user_data,
+			        pj_stun_tx_data **p_tdata)
+{
+    pj_pool_t *pool;
+    pj_status_t status;
+    pj_stun_tx_data *tdata;
+
+    /* Create pool and initialize basic tdata attributes */
+    pool = pj_pool_create(sess->endpt->pf, "tdata%p", 
+			  TDATA_POOL_SIZE, TDATA_POOL_INC, NULL);
+    PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
+
+    tdata = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_tx_data);
+    tdata->pool = pool;
+    tdata->sess = sess;
+    tdata->user_data = tdata;
+
+    /* Create STUN message */
+    status = pj_stun_msg_create(pool, msg_type,  PJ_STUN_MAGIC, 
+				NULL, &tdata->msg);
+    if (status != PJ_SUCCESS) {
+	pj_pool_release(pool);
+	return status;
+    }
+
+    /* If this is a request, then copy the request's transaction ID
+     * as the transaction key.
+     */
+    if (PJ_STUN_IS_REQUEST(msg_type)) {
+	pj_assert(sizeof(tdata->client_key)==sizeof(tdata->msg->hdr.tsx_id));
+	pj_memcpy(tdata->client_key, tdata->msg->hdr.tsx_id,
+		  sizeof(tdata->msg->hdr.tsx_id));
+    }
+
+    *p_tdata = tdata;
+
+    return PJ_SUCCESS;
+}
+
+static void destroy_tdata(pj_stun_tx_data *tdata)
+{
+    if (tdata->client_tsx) {
+	tsx_erase(tdata->sess, tdata);
+	pj_stun_client_tsx_destroy(tdata->client_tsx);
+	tdata->client_tsx = NULL;
+    }
+
+    pj_pool_release(tdata->pool);
+}
+
+static pj_status_t session_apply_req(pj_stun_session *sess,
+				     pj_pool_t *pool,
+				     pj_stun_msg *msg)
+{
+    pj_status_t status;
+
+    /* From draft-ietf-behave-rfc3489bis-05.txt
+     * Section 8.3.1.  Formulating the Request Message
+     */
+    if (sess->realm.slen || sess->username.slen) {
+	pj_stun_generic_string_attr *auname;
+	pj_stun_msg_integrity_attr *amsgi;
+
+	/* Create and add USERNAME attribute */
+	status = pj_stun_generic_string_attr_create(sess->pool, 
+						    PJ_STUN_ATTR_USERNAME,
+						    &sess->username,
+						    &auname);
+	PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+
+	status = pj_stun_msg_add_attr(msg, &auname->hdr);
+	PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+
+	if (sess->realm.slen) {
+	    /* Add REALM only when long term credential is used */
+	    pj_stun_generic_string_attr *arealm;
+	    status = pj_stun_generic_string_attr_create(sess->pool, 
+							PJ_STUN_ATTR_REALM,
+							&sess->realm,
+							&arealm);
+	    PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+
+	    status = pj_stun_msg_add_attr(msg, &arealm->hdr);
+	    PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+	}
+
+	/* Add MESSAGE-INTEGRITY attribute */
+	status = pj_stun_msg_integrity_attr_create(sess->pool, &amsgi);
+	PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+
+	status = pj_stun_msg_add_attr(msg, &amsgi->hdr);
+	PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+
+	PJ_TODO(COMPUTE_MESSAGE_INTEGRITY);
+
+    }
+
+    /* Add FINGERPRINT attribute if necessary */
+    if (sess->fingerprint_enabled) {
+	pj_stun_fingerprint_attr *af;
+
+	status = pj_stun_generic_uint_attr_create(sess->pool, 
+						  PJ_STUN_ATTR_FINGERPRINT,
+						  0, &af);
+	PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+
+	status = pj_stun_msg_add_attr(msg, &af->hdr);
+	PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+
+
+
+
+PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_endpoint *endpt,
+					    const char *name,
+					    const pj_stun_session_cb *cb,
+					    pj_stun_session **p_sess)
+{
+    pj_pool_t	*pool;
+    pj_stun_session *sess;
+
+    PJ_ASSERT_RETURN(endpt && cb && p_sess, PJ_EINVAL);
+
+    pool = pj_pool_create(endpt->pf, name, 4000, 4000, NULL);
+    PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
+
+    sess = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_session);
+    sess->endpt = endpt;
+    sess->pool = pool;
+    pj_memcpy(&sess->cb, cb, sizeof(*cb));
+
+    pj_list_init(&sess->pending_request_list);
+
+    *p_sess = sess;
+
+    PJ_TODO(MUTEX_PROTECTION);
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess)
+{
+    PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+    pj_pool_release(sess->pool);
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_stun_session_set_user_data( pj_stun_session *sess,
+						   void *user_data)
+{
+    PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+    sess->user_data = user_data;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(void*) pj_stun_session_get_user_data(pj_stun_session *sess)
+{
+    PJ_ASSERT_RETURN(sess, NULL);
+    return sess->user_data;
+}
+
+PJ_DEF(pj_status_t) pj_stun_session_set_credential( pj_stun_session *sess,
+						    const pj_str_t *realm,
+						    const pj_str_t *user,
+						    const pj_str_t *passwd)
+{
+    pj_str_t empty = { NULL, 0 };
+
+    PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+    pj_strdup_with_null(sess->pool, &sess->realm, realm ? realm : &empty);
+    pj_strdup_with_null(sess->pool, &sess->username, user ? user : &empty);
+    pj_strdup_with_null(sess->pool, &sess->password, passwd ? passwd : &empty);
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_stun_session_enable_fingerprint(pj_stun_session *sess,
+						       pj_bool_t enabled)
+{
+    PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+    sess->fingerprint_enabled = enabled;
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_stun_session_create_bind_req(pj_stun_session *sess,
+						    pj_stun_tx_data **p_tdata)
+{
+    pj_pool_t *pool;
+    pj_stun_tx_data *tdata;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL);
+
+    status = create_tdata(sess, PJ_STUN_BINDING_REQUEST, NULL, &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = session_apply_req(sess, pool, tdata->msg);
+    if (status != PJ_SUCCESS) {
+	destroy_tdata(tdata);
+	return status;
+    }
+
+    *p_tdata = tdata;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_stun_session_create_allocate_req(pj_stun_session *sess,
+							pj_stun_tx_data **p_tdata)
+{
+    PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
+}
+
+
+PJ_DEF(pj_status_t) 
+pj_stun_session_create_set_active_destination_req(pj_stun_session *sess,
+						  pj_stun_tx_data **p_tdata)
+{
+    PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
+}
+
+PJ_DEF(pj_status_t) pj_stun_session_create_connect_req(	pj_stun_session *sess,
+						        pj_stun_tx_data **p_tdata)
+{
+    PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
+}
+
+PJ_DEF(pj_status_t) 
+pj_stun_session_create_connection_status_ind(pj_stun_session *sess,
+					     pj_stun_tx_data **p_tdata)
+{
+    PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
+}
+
+PJ_DEF(pj_status_t) pj_stun_session_create_send_ind( pj_stun_session *sess,
+						     pj_stun_tx_data **p_tdata)
+{
+    PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
+}
+
+PJ_DEF(pj_status_t) pj_stun_session_create_data_ind( pj_stun_session *sess,
+						     pj_stun_tx_data **p_tdata)
+{
+    PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
+}
+
+PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
+					      unsigned addr_len,
+					      const pj_sockaddr_t *server,
+					      pj_stun_tx_data *tdata)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(sess && addr_len && server && tdata, PJ_EINVAL);
+
+    if (PJ_LOG_MAX_LEVEL >= 5) {
+	char buf[512];
+	unsigned buflen = sizeof(buf);
+	const char *dst_name;
+	int dst_port;
+	const pj_sockaddr *dst = (const pj_sockaddr*)server;
+	
+	if (dst->sa_family == PJ_AF_INET) {
+	    const pj_sockaddr_in *dst4 = (const pj_sockaddr_in*)dst;
+	    dst_name = pj_inet_ntoa(dst4->sin_addr);
+	    dst_port = pj_ntohs(dst4->sin_port);
+	} else if (dst->sa_family == PJ_AF_INET6) {
+	    const pj_sockaddr_in6 *dst6 = (const pj_sockaddr_in6*)dst;
+	    dst_name = "IPv6";
+	    dst_port = pj_ntohs(dst6->sin6_port);
+	} else {
+	    LOG_ERR_(sess, "Invalid address family", PJ_EINVAL);
+	    return PJ_EINVAL;
+	}
+
+	PJ_LOG(5,(SNAME(sess), 
+		  "Sending STUN message to %s:%d:\n"
+		  "%s\n",
+		  dst_name, dst_port,
+		  pj_stun_msg_dump(tdata->msg, buf, &buflen)));
+    }
+
+
+    /* If this is a STUN request message, then send the request with
+     * a new STUN client transaction.
+     */
+    if (PJ_STUN_IS_REQUEST(tdata->msg->hdr.type)) {
+
+	/* Create STUN client transaction */
+	status = pj_stun_client_tsx_create(sess->endpt, &tsx_cb, 
+					   &tdata->client_tsx);
+	PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+	pj_stun_client_tsx_set_data(tdata->client_tsx, (void*)tdata);
+
+	/* Send the request! */
+	status = pj_stun_client_tsx_send_msg(tdata->client_tsx, PJ_TRUE,
+					     tdata->msg);
+	if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	    LOG_ERR_(sess, "Error sending STUN request", status);
+	    return status;
+	}
+
+	/* Add to pending request list */
+	tsx_add(sess, tdata);
+
+    } else {
+	/* Otherwise for non-request message, send directly to transport. */
+	status = sess->cb.on_send_msg(tdata, addr_len, server);
+    }
+
+
+    return status;
+}
+
+
+PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess,
+					      const void *packet,
+					      pj_size_t pkt_size,
+					      unsigned *parsed_len)
+{
+    pj_stun_msg *msg;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(sess && packet && pkt_size, PJ_EINVAL);
+
+    /* Try to parse the message */
+    status = pj_stun_msg_decode(tsx->pool, (const pj_uint8_t*)packet,
+			        pkt_size, 0, &msg, parsed_len,
+				NULL, NULL, NULL);
+    if (status != PJ_SUCCESS) {
+	LOG_ERR_(sess, "STUN msg_decode() error", status);
+	return status;
+    }
+
+    if (PJ_STUN_IS_RESPONSE(msg->hdr.type) ||
+	PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
+    {
+	pj_stun_tx_data *tdata;
+
+	/* Lookup pending client transaction */
+	tdata = tsx_lookup(sess, msg);
+	if (tdata == NULL) {
+	    LOG_ERR_(sess, "STUN error finding transaction", PJ_ENOTFOUND);
+	    return PJ_ENOTFOUND;
+	}
+
+	/* Pass the response to the transaction. 
+	 * If the message is accepted, transaction callback will be called,
+	 * and this will call the session callback too.
+	 */
+	status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	/* If transaction has completed, destroy the transmit data.
+	 * This will remove the transaction from the pending list too.
+	 */
+	if (pj_stun_client_tsx_is_complete(tdata->client_tsx)) {
+	    destroy_tdata(tdata);
+	    tdata = NULL;
+	}
+
+	return PJ_SUCCESS;
+
+    } else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) {
+
+
+    } else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) {
+
+
+    } else {
+	pj_assert(!"Unexpected!");
+	return PJ_EBUG;
+    }
+
+}
+
diff --git a/pjlib-util/src/pjstun-client/stun_session.h b/pjlib-util/src/pjstun-client/stun_session.h
new file mode 100644
index 0000000..f9759cb
--- /dev/null
+++ b/pjlib-util/src/pjstun-client/stun_session.h
@@ -0,0 +1,114 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2005 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 __PJLIB_UTIL_STUN_SESSION_H__
+#define __PJLIB_UTIL_STUN_SESSION_H__
+
+#include <pjlib-util/stun_msg.h>
+#include <pjlib-util/stun_endpoint.h>
+#include <pjlib-util/stun_transaction.h>
+#include <pj/list.h>
+
+
+typedef struct pj_stun_tx_data pj_stun_tx_data;
+typedef struct pj_stun_session pj_stun_session;
+
+typedef struct pj_stun_session_cb
+{
+    pj_status_t (*on_send_msg)(pj_stun_tx_data *tdata,
+			       unsigned addr_len, 
+			       const pj_sockaddr_t *dst_addr);
+
+    void (*on_bind_response)(void *user_data, pj_status_t status, pj_stun_msg *response);
+    void (*on_allocate_response)(void *user_data, pj_status_t status, pj_stun_msg *response);
+    void (*on_set_active_destination_response)(void *user_data, pj_status_t status, pj_stun_msg *response);
+    void (*on_connect_response)(void *user_data, pj_status_t status, pj_stun_msg *response);
+} pj_stun_session_cb;
+
+
+struct pj_stun_tx_data
+{
+    PJ_DECL_LIST_MEMBER(struct pj_stun_tx_data);
+
+    pj_pool_t		*pool;
+    pj_stun_session	*sess;
+    pj_stun_msg		*msg;
+    void		*user_data;
+
+    pj_stun_client_tsx	*client_tsx;
+    pj_uint8_t		 client_key[12];
+};
+
+
+PJ_DECL(pj_status_t) pj_stun_session_create(pj_stun_endpoint *endpt,
+					    const char *name,
+					    const pj_stun_session_cb *cb,
+					    pj_stun_session **p_sess);
+
+PJ_DECL(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess);
+
+PJ_DECL(pj_status_t) pj_stun_session_set_user_data(pj_stun_session *sess,
+						   void *user_data);
+
+PJ_DECL(void*) pj_stun_session_get_user_data(pj_stun_session *sess);
+
+PJ_DECL(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess,
+						    const pj_str_t *realm,
+						    const pj_str_t *user,
+						    const pj_str_t *passwd);
+
+PJ_DECL(pj_status_t) pj_stun_session_enable_fingerprint(pj_stun_session *sess,
+							pj_bool_t enabled);
+
+PJ_DECL(pj_status_t) pj_stun_session_create_bind_req(pj_stun_session *sess,
+						     pj_stun_tx_data **p_tdata);
+
+PJ_DECL(pj_status_t) pj_stun_session_create_allocate_req(pj_stun_session *sess,
+							 pj_stun_tx_data **p_tdata);
+
+PJ_DECL(pj_status_t) 
+pj_stun_session_create_set_active_destination_req(pj_stun_session *sess,
+						  pj_stun_tx_data **p_tdata);
+
+PJ_DECL(pj_status_t) pj_stun_session_create_connect_req(pj_stun_session *sess,
+							pj_stun_tx_data **p_tdata);
+
+PJ_DECL(pj_status_t) 
+pj_stun_session_create_connection_status_ind(pj_stun_session *sess,
+					     pj_stun_tx_data **p_tdata);
+
+PJ_DECL(pj_status_t) pj_stun_session_create_send_ind(pj_stun_session *sess,
+						     pj_stun_tx_data **p_tdata);
+
+PJ_DECL(pj_status_t) pj_stun_session_create_data_ind(pj_stun_session *sess,
+						     pj_stun_tx_data **p_tdata);
+
+PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess,
+					      unsigned addr_len,
+					      const pj_sockaddr_t *server,
+					      pj_stun_tx_data *tdata);
+
+PJ_DECL(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess,
+					       const void *packet,
+					       pj_size_t pkt_size,
+					       unsigned *parsed_len);
+
+
+
+#endif	/* __PJLIB_UTIL_STUN_SESSION_H__ */
+