Index: /pjproject/branches/iceproject/pjlib-util/src/pjstun-srv/server.h
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/src/pjstun-srv/server.h	(revision 991)
+++ /pjproject/branches/iceproject/pjlib-util/src/pjstun-srv/server.h	(revision 991)
@@ -0,0 +1,61 @@
+/* $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 __PJSTUN_SERVER_H__
+#define __PJSTUN_SERVER_H__
+
+
+#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;
+
+    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];
+};
+
+struct stun_server_tag
+{
+    pj_caching_pool	 cp;
+    pj_pool_t		*pool;
+    pj_ioqueue_t	*ioqueue;
+    unsigned		 service_cnt;
+    struct service	 services[MAX_SERVICE];
+
+    pj_bool_t		 thread_quit_flag;
+    unsigned		 thread_cnt;
+    pj_thread_t		*threads[16];
+
+};
+
+extern struct stun_server_tag server;
+
+
+#endif	/* __PJSTUN_SERVER_H__ */
+
Index: /pjproject/branches/iceproject/pjlib-util/src/pjstun-srv/server_main.c
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/src/pjstun-srv/server_main.c	(revision 991)
+++ /pjproject/branches/iceproject/pjlib-util/src/pjstun-srv/server_main.c	(revision 991)
@@ -0,0 +1,443 @@
+/* $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>
+#include "server.h"
+
+#include <stdio.h>
+#include <conio.h>
+
+
+#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)
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+    pj_strerror(status, errmsg, sizeof(errmsg));
+
+    PJ_LOG(3,(sender, "%s: %s", title, errmsg));
+
+    return status;
+}
+
+
+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_stun_error_code_attr *err_attr;
+    pj_status_t status;
+
+    /* Create response or error response */
+    if (err_code)
+	msg_type |= PJ_STUN_ERROR_RESPONSE_BIT;
+    else
+	msg_type |= PJ_STUN_RESPONSE_BIT;
+
+    status = pj_stun_msg_create(pool, msg_type, req_msg->hdr.magic, 
+				req_msg->hdr.tsx_id, &response);
+    if (status != PJ_SUCCESS) {
+	return status;
+    }
+
+    /* Add error code attribute */
+    if (err_code) {
+	status = pj_stun_error_code_attr_create(pool, err_code, NULL,
+						&err_attr);
+	if (status != PJ_SUCCESS) {
+	    return status;
+	}
+
+	pj_stun_msg_add_attr(response, &err_attr->hdr);
+    }
+
+    /* 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;
+    pj_ssize_t length;
+    pj_status_t status;
+
+    /* Encode packet */
+    tx_pkt_len = sizeof(svc->tx_pkt);
+    status = pj_stun_msg_encode(msg, svc->tx_pkt, tx_pkt_len, 0,
+				&tx_pkt_len);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    length = tx_pkt_len;
+
+    /* Send packet */
+    if (svc->is_stream) {
+	status = pj_ioqueue_send(svc->key, &svc->send_opkey, svc->tx_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);
+    }
+
+    return (status == PJ_SUCCESS || status == PJ_EPENDING) ? 
+	    PJ_SUCCESS : status;
+}
+
+
+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);
+    if (status != PJ_SUCCESS) {
+	server_perror(THIS_FILE, "Error creating response", status);
+	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) {
+	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);
+	if (status != PJ_SUCCESS) {
+	    server_perror(THIS_FILE, "Error creating response", status);
+	    return;
+	}
+    }
+
+    /* 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);
+}
+
+
+static void on_read_complete(pj_ioqueue_key_t *key, 
+			     pj_ioqueue_op_key_t *op_key, 
+			     pj_ssize_t bytes_read)
+{
+    struct service *svc = (struct service *) pj_ioqueue_get_user_data(key);
+    pj_pool_t *pool = NULL;
+    pj_stun_msg *rx_msg;
+    unsigned err_code;
+    unsigned uattr_cnt;
+    pj_uint16_t uattr_types[16];
+    pj_status_t status;
+
+    if (bytes_read <= 0)
+	goto next_read;
+
+    pool = pj_pool_create(&server.cp.factory, "service", 4000, 4000, NULL);
+
+    err_code = 0;
+    uattr_cnt = PJ_ARRAY_SIZE(uattr_types);
+    rx_msg = NULL;
+    status = pj_stun_msg_decode(pool, svc->rx_pkt, bytes_read, 0, &rx_msg,
+				NULL, &err_code, &uattr_cnt, uattr_types);
+    if (status != PJ_SUCCESS) {
+	if (err_code != 0 && rx_msg && PJ_STUN_IS_REQUEST(rx_msg->hdr.type)) {
+	    err_respond(svc, pool, rx_msg, err_code, 
+			uattr_cnt, uattr_types);
+	}
+	goto next_read;
+    }
+
+    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);
+	}
+	
+    }
+
+next_read:
+    if (pool != NULL)
+	pj_pool_release(pool);
+
+    if (bytes_read < 0) {
+	server_perror(THIS_FILE, "on_read_complete()", -bytes_read);
+    }
+
+    svc->rx_pkt_len = sizeof(svc->rx_pkt);
+    svc->src_addr_len = sizeof(svc->src_addr);
+
+    status = pj_ioqueue_recvfrom(svc->key, &svc->recv_opkey, 
+				 svc->rx_pkt, &svc->rx_pkt_len, 
+				 PJ_IOQUEUE_ALWAYS_ASYNC,
+				 &svc->src_addr, &svc->src_addr_len);
+    if (status != PJ_EPENDING)
+	server_perror(THIS_FILE, "error starting async read", status);
+}
+
+
+static pj_status_t init_service(struct service *svc)
+{
+    pj_status_t status;
+    pj_ioqueue_callback service_callback;
+    pj_sockaddr_in addr;
+
+    status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &svc->sock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = pj_sockaddr_in_init(&addr, NULL, svc->port);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+    
+    status = pj_sock_bind(svc->sock, &addr, sizeof(addr));
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    pj_bzero(&service_callback, sizeof(service_callback));
+    service_callback.on_read_complete = &on_read_complete;
+
+    status = pj_ioqueue_register_sock(server.pool, server.ioqueue, svc->sock,
+				      svc, &service_callback, &svc->key);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+
+    pj_ioqueue_op_key_init(&svc->recv_opkey, sizeof(svc->recv_opkey));
+    pj_ioqueue_op_key_init(&svc->send_opkey, sizeof(svc->send_opkey));
+
+    on_read_complete(svc->key, &svc->recv_opkey, 0);
+
+    PJ_LOG(4,(THIS_FILE, "Service started on port %d", svc->port));
+    return PJ_SUCCESS;
+
+on_error:
+    if (svc->key != NULL) {
+	pj_ioqueue_unregister(svc->key);
+	svc->key = NULL;
+	svc->sock = PJ_INVALID_SOCKET;
+    } else if (svc->sock != 0 && svc->sock != PJ_INVALID_SOCKET) {
+	pj_sock_close(svc->sock);
+	svc->sock = PJ_INVALID_SOCKET;
+    }
+
+    return status;
+}
+
+
+static int worker_thread(void *p)
+{
+    PJ_UNUSED_ARG(p);
+
+    while (!server.thread_quit_flag) {
+	pj_time_val timeout = { 0, 50 };
+	pj_ioqueue_poll(server.ioqueue, &timeout);
+    }
+
+    return 0;
+}
+
+
+pj_status_t server_init(void)
+{
+    pj_status_t status;
+
+    status = pj_init();
+    if (status != PJ_SUCCESS)
+	return server_perror(THIS_FILE, "pj_init() error", status);
+
+    status = pjlib_util_init();
+    if (status != PJ_SUCCESS)
+	return server_perror(THIS_FILE, "pjlib_util_init() error", status);
+
+    pj_caching_pool_init(&server.cp, 
+			 &pj_pool_factory_default_policy, 0);
+
+
+    server.pool = pj_pool_create(&server.cp.factory, "server", 4000, 4000,
+				 NULL);
+
+    status = pj_ioqueue_create(server.pool, PJ_IOQUEUE_MAX_HANDLES, 
+			       &server.ioqueue);
+    if (status != PJ_SUCCESS)
+	return server_perror(THIS_FILE, "pj_ioqueue_create()", status);
+
+    server.service_cnt = 1;
+    server.services[0].index = 0;
+    server.services[0].port = PJ_STUN_PORT;
+
+    status = init_service(&server.services[0]);
+    if (status != PJ_SUCCESS)
+	return server_perror(THIS_FILE, "init_service() error", status);
+
+    return PJ_SUCCESS;
+}
+
+
+pj_status_t server_main(void)
+{
+#if 1
+    for (;;) {
+	pj_time_val timeout = { 0, 50 };
+	pj_ioqueue_poll(server.ioqueue, &timeout);
+
+	if (kbhit() && _getch()==27)
+	    break;
+    }
+#else
+    pj_status_t status;
+    char line[10];
+
+    status = pj_thread_create(server.pool, "stun_server", &worker_thread, NULL,
+			      0, 0, &server.threads[0]);
+    if (status != PJ_SUCCESS)
+	return server_perror(THIS_FILE, "create_thread() error", status);
+
+    puts("Press ENTER to quit");
+    fgets(line, sizeof(line), stdin);
+
+#endif
+
+    return PJ_SUCCESS;
+}
+
+
+pj_status_t server_destroy(void)
+{
+    unsigned i;
+
+    for (i=0; i<PJ_ARRAY_SIZE(server.services); ++i) {
+	struct service *svc = &server.services[i];
+
+	if (svc->key != NULL) {
+	    pj_ioqueue_unregister(svc->key);
+	    svc->key = NULL;
+	    svc->sock = PJ_INVALID_SOCKET;
+	} else if (svc->sock != 0 && svc->sock != PJ_INVALID_SOCKET) {
+	    pj_sock_close(svc->sock);
+	    svc->sock = PJ_INVALID_SOCKET;
+	}
+    }
+
+    server.thread_quit_flag = PJ_TRUE;
+    for (i=0; i<PJ_ARRAY_SIZE(server.threads); ++i) {
+	if (server.threads[i]) {
+	    pj_thread_join(server.threads[i]);
+	    pj_thread_destroy(server.threads[i]);
+	    server.threads[i] = NULL;
+	}
+    }
+
+    pj_ioqueue_destroy(server.ioqueue);
+    pj_pool_release(server.pool);
+    pj_caching_pool_destroy(&server.cp);
+
+    pj_shutdown();
+
+    return PJ_SUCCESS;
+}
+
+
+int main()
+{
+    if (server_init()) {
+	server_destroy();
+	return 1;
+    }
+
+    server_main();
+    server_destroy();
+
+    return 0;
+}
