Index: pjproject/trunk/pjlib-util/src/pjstun-srv-test/server_main.c
===================================================================
--- pjproject/trunk/pjlib-util/src/pjstun-srv-test/server_main.c	(revision 1004)
+++ pjproject/trunk/pjlib-util/src/pjstun-srv-test/server_main.c	(revision 1021)
@@ -19,5 +19,4 @@
 #include <pjlib-util.h>
 #include <pjlib.h>
-#include "server.h"
 
 #include <stdio.h>
@@ -27,10 +26,45 @@
 #define THIS_FILE	"server_main.c"
 #define MAX_THREADS	8
-
-struct stun_server_tag server;
-
-
-pj_status_t server_perror(const char *sender, const char *title, 
-			  pj_status_t status)
+#define MAX_SERVICE	16
+#define MAX_PKT_LEN	512
+
+struct service
+{
+    unsigned		 index;
+    pj_uint16_t		 port;
+    pj_bool_t		 is_stream;
+    pj_sock_t		 sock;
+    pj_ioqueue_key_t	*key;
+    pj_ioqueue_op_key_t  recv_opkey,
+			 send_opkey;
+
+    pj_stun_session	*sess;
+
+    int		         src_addr_len;
+    pj_sockaddr_in	 src_addr;
+    pj_ssize_t		 rx_pkt_len;
+    pj_uint8_t		 rx_pkt[MAX_PKT_LEN];
+    pj_uint8_t		 tx_pkt[MAX_PKT_LEN];
+};
+
+static struct stun_server
+{
+    pj_caching_pool	 cp;
+    pj_pool_t		*pool;
+    pj_stun_endpoint	*endpt;
+    pj_ioqueue_t	*ioqueue;
+    pj_timer_heap_t	*timer_heap;
+    unsigned		 service_cnt;
+    struct service	 services[MAX_SERVICE];
+
+    pj_bool_t		 thread_quit_flag;
+    unsigned		 thread_cnt;
+    pj_thread_t		*threads[16];
+
+} server;
+
+
+static pj_status_t server_perror(const char *sender, const char *title, 
+				 pj_status_t status)
 {
     char errmsg[PJ_ERR_MSG_SIZE];
@@ -43,72 +77,35 @@
 
 
-static pj_status_t create_response(pj_pool_t *pool,
-				   const pj_stun_msg *req_msg,
-				   unsigned err_code,
-				   unsigned uattr_cnt,
-				   pj_uint16_t uattr_types[],
-				   pj_stun_msg **p_response)
-{
-    pj_uint32_t msg_type = req_msg->hdr.type;
-    pj_stun_msg *response;
-    pj_status_t status;
-
-    status = pj_stun_msg_create_response(pool, req_msg, err_code, NULL,
-					 &response);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    /* Add unknown_attribute attributes if err_code is 420 */
-    if (err_code == PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE) {
-	pj_stun_unknown_attr *uattr;
-
-	status = pj_stun_unknown_attr_create(pool, uattr_cnt, uattr_types,
-					     &uattr);
-	if (status != PJ_SUCCESS)
-	    return status;
-
-	pj_stun_msg_add_attr(response, &uattr->hdr);
-    }
-
-    *p_response = response;
-    return PJ_SUCCESS;
-}
-
-
-static pj_status_t send_msg(struct service *svc, const pj_stun_msg *msg)
-{
-    unsigned tx_pkt_len;
+/* Callback to be called to send outgoing message */
+static pj_status_t on_send_msg(pj_stun_session *sess,
+			       const void *pkt,
+			       pj_size_t pkt_size,
+			       const pj_sockaddr_t *dst_addr,
+			       unsigned addr_len)
+{
+    struct service *svc;
     pj_ssize_t length;
     pj_status_t status;
 
-    /* Print to log */
-    PJ_LOG(4,(THIS_FILE, "TX STUN message: \n"
-			 "--- begin STUN message ---\n"
-			 "%s"
-			 "--- end of STUN message ---\n", 
-	      pj_stun_msg_dump(msg, svc->tx_pkt, sizeof(svc->tx_pkt), NULL)));
-
-    /* Encode packet */
-    tx_pkt_len = sizeof(svc->tx_pkt);
-    status = pj_stun_msg_encode(msg, svc->tx_pkt, tx_pkt_len, 0,
-				NULL, &tx_pkt_len);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    length = tx_pkt_len;
+    svc = (struct service*) pj_stun_session_get_user_data(sess);
 
     /* Send packet */
+    length = pkt_size;
     if (svc->is_stream) {
-	status = pj_ioqueue_send(svc->key, &svc->send_opkey, svc->tx_pkt,
-				 &length, 0);
+	status = pj_ioqueue_send(svc->key, &svc->send_opkey, pkt, &length, 0);
     } else {
-	status = pj_ioqueue_sendto(svc->key, &svc->send_opkey, svc->tx_pkt,
-				   &length, 0, &svc->src_addr, 
-				   svc->src_addr_len);
-    }
-
-    PJ_LOG(4,(THIS_FILE, "Sending STUN %s %s",
-	      pj_stun_get_method_name(msg->hdr.type),
-	      pj_stun_get_class_name(msg->hdr.type)));
+#if 0
+	pj_pool_t *pool;
+	char *buf;
+	pj_stun_msg *msg;
+
+	pool = pj_pool_create(&server.cp.factory, "", 4000, 4000, NULL);
+	status = pj_stun_msg_decode(pool, pkt, pkt_size, PJ_STUN_CHECK_PACKET, &msg, NULL, NULL);
+	buf = pj_pool_alloc(pool, 512);
+	PJ_LOG(3,("", "%s", pj_stun_msg_dump(msg, buf, 512, NULL)));
+#endif
+	status = pj_ioqueue_sendto(svc->key, &svc->send_opkey, pkt, &length, 
+				   0, dst_addr, addr_len);
+    }
 
     return (status == PJ_SUCCESS || status == PJ_EPENDING) ? 
@@ -117,86 +114,110 @@
 
 
-static pj_status_t err_respond(struct service *svc,
-			       pj_pool_t *pool,
-			       const pj_stun_msg *req_msg,
-			       unsigned err_code,
-			       unsigned uattr_cnt,
-			       pj_uint16_t uattr_types[])
-{
-    pj_stun_msg *response;
-    pj_status_t status;
-
-    /* Create the error response */
-    status = create_response(pool, req_msg, err_code,
-			     uattr_cnt, uattr_types, &response);
+/* Handle STUN binding request */
+static pj_status_t on_rx_binding_request(pj_stun_session *sess,
+					 const pj_uint8_t *pkt,
+					 unsigned pkt_len,
+					 const pj_stun_msg *msg,
+					 const pj_sockaddr_t *src_addr,
+					 unsigned src_addr_len)
+{
+    struct service *svc = (struct service *) pj_stun_session_get_user_data(sess);
+    pj_stun_tx_data *tdata;
+    pj_stun_auth_policy pol;
+    pj_status_t status;
+
+    /* Create response */
+    status = pj_stun_session_create_response(sess, msg, 0, NULL, &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
+
+#if 1
+    pj_memset(&pol, 0, sizeof(pol));
+    pol.type = PJ_STUN_POLICY_STATIC_LONG_TERM;
+    pol.user_data = NULL;
+    pol.data.static_long_term.realm = pj_str("realm");
+    pol.data.static_long_term.username = pj_str("user");
+    pol.data.static_long_term.password = pj_str("password");
+    pol.data.static_long_term.nonce = pj_str("nonce");
+    status = pj_stun_verify_credential(pkt, pkt_len, msg, &pol, tdata->pool, 
+				       &tdata->msg);
+    if (!tdata->msg)
+	return status;
+#endif
+
+    /* Create MAPPED-ADDRESS attribute */
+    status = pj_stun_msg_add_generic_ip_addr_attr(tdata->pool, tdata->msg,
+						  PJ_STUN_ATTR_MAPPED_ADDR,
+						  PJ_FALSE,
+					          src_addr, src_addr_len);
     if (status != PJ_SUCCESS) {
 	server_perror(THIS_FILE, "Error creating response", status);
+	pj_stun_msg_destroy_tdata(sess, tdata);
 	return status;
     }
 
-    /* Send response */
-    status = send_msg(svc, response);
-    if (status != PJ_SUCCESS) {
-	server_perror(THIS_FILE, "Error sending response", status);
-	return status;
-    }
-
-    return PJ_SUCCESS;
-}
-
-
-static void handle_binding_request(struct service *svc, pj_pool_t *pool, 
-				   const pj_stun_msg *rx_msg)
-{
-    pj_stun_msg *response;
-    pj_stun_generic_ip_addr_attr *m_attr;
-    pj_status_t status;
-
-    status = create_response(pool, rx_msg, 0, 0, NULL, &response);
-    if (status != PJ_SUCCESS) {
-	server_perror(THIS_FILE, "Error creating response", status);
-	return;
-    }
-
-    /* Create MAPPED-ADDRESS attribute */
-    status = pj_stun_generic_ip_addr_attr_create(pool,
-						 PJ_STUN_ATTR_MAPPED_ADDR,
-					         PJ_FALSE,
-					         svc->src_addr_len,
-					         &svc->src_addr, &m_attr);
-    if (status != PJ_SUCCESS) {
-	server_perror(THIS_FILE, "Error creating response", status);
-	return;
-    }
-    pj_stun_msg_add_attr(response, &m_attr->hdr);
-
     /* On the presence of magic, create XOR-MAPPED-ADDRESS attribute */
-    if (rx_msg->hdr.magic == PJ_STUN_MAGIC) {
+    if (msg->hdr.magic == PJ_STUN_MAGIC) {
 	status = 
-	    pj_stun_generic_ip_addr_attr_create(pool, 
-						PJ_STUN_ATTR_XOR_MAPPED_ADDRESS,
-						PJ_TRUE,
-						svc->src_addr_len,
-						&svc->src_addr, &m_attr);
+	    pj_stun_msg_add_generic_ip_addr_attr(tdata->pool, tdata->msg,
+						 PJ_STUN_ATTR_XOR_MAPPED_ADDRESS,
+						 PJ_TRUE,
+						 src_addr, src_addr_len);
 	if (status != PJ_SUCCESS) {
 	    server_perror(THIS_FILE, "Error creating response", status);
-	    return;
+	    pj_stun_msg_destroy_tdata(sess, tdata);
+	    return status;
 	}
     }
 
     /* Send */
-    status = send_msg(svc, response);
-    if (status != PJ_SUCCESS)
-	server_perror(THIS_FILE, "Error sending response", status);
-}
-
-
-static void handle_unknown_request(struct service *svc, pj_pool_t *pool, 
-				   pj_stun_msg *rx_msg)
-{
-    err_respond(svc, pool, rx_msg, PJ_STUN_STATUS_BAD_REQUEST, 0, NULL);
-}
-
-
+    status = pj_stun_session_send_msg(sess, 0, src_addr, src_addr_len, tdata);
+    return status;
+}
+
+
+/* Handle unknown request */
+static pj_status_t on_rx_unknown_request(pj_stun_session *sess,
+					 const pj_uint8_t *pkt,
+					 unsigned pkt_len,
+					 const pj_stun_msg *msg,
+					 const pj_sockaddr_t *src_addr,
+					 unsigned src_addr_len)
+{
+    pj_stun_tx_data *tdata;
+    pj_status_t status;
+
+    /* Create response */
+    status = pj_stun_session_create_response(sess, msg, 
+					     PJ_STUN_STATUS_BAD_REQUEST,
+					     NULL, &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Send */
+    status = pj_stun_session_send_msg(sess, 0, src_addr, src_addr_len, tdata);
+    return status;
+}
+
+/* Callback to be called by STUN session on incoming STUN requests */
+static pj_status_t on_rx_request(pj_stun_session *sess,
+				 const pj_uint8_t *pkt,
+				 unsigned pkt_len,
+				 const pj_stun_msg *msg,
+				 const pj_sockaddr_t *src_addr,
+				 unsigned src_addr_len)
+{
+    switch (PJ_STUN_GET_METHOD(msg->hdr.type)) {
+    case PJ_STUN_BINDING_METHOD:
+	return on_rx_binding_request(sess, pkt, pkt_len, msg, 
+				     src_addr, src_addr_len);
+    default:
+	return on_rx_unknown_request(sess, pkt, pkt_len, msg,
+				     src_addr, src_addr_len);
+    }
+}
+
+
+/* Callback on ioqueue read completion */
 static void on_read_complete(pj_ioqueue_key_t *key, 
 			     pj_ioqueue_op_key_t *op_key, 
@@ -204,7 +225,4 @@
 {
     struct service *svc = (struct service *) pj_ioqueue_get_user_data(key);
-    pj_pool_t *pool = NULL;
-    pj_stun_msg *rx_msg, *response;
-    char dump[512];
     pj_status_t status;
 
@@ -212,38 +230,13 @@
 	goto next_read;
 
-    pool = pj_pool_create(&server.cp.factory, "service", 4000, 4000, NULL);
-
-    rx_msg = NULL;
-    status = pj_stun_msg_decode(pool, svc->rx_pkt, bytes_read, 0, &rx_msg,
-				NULL, &response);
+    /* Handle packet to session */
+    status = pj_stun_session_on_rx_pkt(svc->sess, svc->rx_pkt, bytes_read,
+				       PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET,
+				       NULL, &svc->src_addr, svc->src_addr_len);
     if (status != PJ_SUCCESS) {
-	server_perror(THIS_FILE, "STUN msg_decode() error", status);
-	if (response) {
-	    send_msg(svc, response);
-	}
-	goto next_read;
-    }
-
-    PJ_LOG(4,(THIS_FILE, "RX STUN message: \n"
-			 "--- begin STUN message ---\n"
-			 "%s"
-			 "--- end of STUN message ---\n", 
-	      pj_stun_msg_dump(rx_msg, dump, sizeof(dump), NULL)));
-
-    if (PJ_STUN_IS_REQUEST(rx_msg->hdr.type)) {
-	switch (rx_msg->hdr.type) {
-	case PJ_STUN_BINDING_REQUEST:
-	    handle_binding_request(svc, pool, rx_msg);
-	    break;
-	default:
-	    handle_unknown_request(svc, pool, rx_msg);
-	}
-	
+	server_perror(THIS_FILE, "Error processing incoming packet", status);
     }
 
 next_read:
-    if (pool != NULL)
-	pj_pool_release(pool);
-
     if (bytes_read < 0) {
 	server_perror(THIS_FILE, "on_read_complete()", -bytes_read);
@@ -266,4 +259,5 @@
     pj_status_t status;
     pj_ioqueue_callback service_callback;
+    pj_stun_session_cb sess_cb;
     pj_sockaddr_in addr;
 
@@ -279,4 +273,14 @@
     if (status != PJ_SUCCESS)
 	goto on_error;
+
+    pj_bzero(&sess_cb, sizeof(sess_cb));
+    sess_cb.on_send_msg = &on_send_msg;
+    sess_cb.on_rx_request = &on_rx_request;
+    status = pj_stun_session_create(server.endpt, "session", 
+				    &sess_cb, &svc->sess);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    pj_stun_session_set_user_data(svc->sess, (void*)svc);
 
     pj_bzero(&service_callback, sizeof(service_callback));
@@ -317,4 +321,5 @@
     while (!server.thread_quit_flag) {
 	pj_time_val timeout = { 0, 50 };
+	pj_timer_heap_poll(server.timer_heap, NULL);
 	pj_ioqueue_poll(server.ioqueue, &timeout);
     }
@@ -347,4 +352,14 @@
     if (status != PJ_SUCCESS)
 	return server_perror(THIS_FILE, "pj_ioqueue_create()", status);
+
+    status = pj_timer_heap_create(server.pool, 1024, &server.timer_heap);
+    if (status != PJ_SUCCESS)
+	return server_perror(THIS_FILE, "Error creating timer heap", status);
+
+    status = pj_stun_endpoint_create(&server.cp.factory, 0, 
+				     server.ioqueue, server.timer_heap, 
+				     &server.endpt);
+    if (status != PJ_SUCCESS)
+	return server_perror(THIS_FILE, "Error creating endpoint", status);
 
     server.service_cnt = 1;
@@ -362,7 +377,8 @@
 pj_status_t server_main(void)
 {
-#if 1
+#if 0
     for (;;) {
 	pj_time_val timeout = { 0, 50 };
+	pj_timer_heap_poll(server.timer_heap, NULL);
 	pj_ioqueue_poll(server.ioqueue, &timeout);
 
@@ -414,6 +430,10 @@
     }
 
+    pj_stun_session_destroy(server.services[0].sess);
+    pj_stun_endpoint_destroy(server.endpt);
     pj_ioqueue_destroy(server.ioqueue);
     pj_pool_release(server.pool);
+
+    pj_pool_factory_dump(&server.cp.factory, PJ_TRUE);
     pj_caching_pool_destroy(&server.cp);
 
