From f3ab456a17af1c89a6e3be4d20c5944853df1cb0 Mon Sep 17 00:00:00 2001 From: "David M. Lee" Date: Mon, 7 Jan 2013 14:24:28 -0600 Subject: Import pjproject-2.0.1 --- pjlib/src/pj/activesock.c | 882 ++++++++++ pjlib/src/pj/addr_resolv_linux_kernel.c | 26 + pjlib/src/pj/addr_resolv_sock.c | 282 ++++ pjlib/src/pj/addr_resolv_symbian.cpp | 201 +++ pjlib/src/pj/array.c | 71 + pjlib/src/pj/compat/longjmp_i386.S | 42 + pjlib/src/pj/compat/setjmp_i386.S | 61 + pjlib/src/pj/compat/sigjmp.c | 40 + pjlib/src/pj/compat/string.c | 45 + pjlib/src/pj/compat/string_compat.c | 83 + pjlib/src/pj/config.c | 89 + pjlib/src/pj/ctype.c | 28 + pjlib/src/pj/errno.c | 320 ++++ pjlib/src/pj/except.c | 185 ++ pjlib/src/pj/exception_symbian.cpp | 116 ++ pjlib/src/pj/extra-exports.c | 41 + pjlib/src/pj/fifobuf.c | 189 +++ pjlib/src/pj/file_access_unistd.c | 114 ++ pjlib/src/pj/file_access_win32.c | 215 +++ pjlib/src/pj/file_io_ansi.c | 170 ++ pjlib/src/pj/file_io_win32.c | 240 +++ pjlib/src/pj/guid.c | 27 + pjlib/src/pj/guid_simple.c | 81 + pjlib/src/pj/guid_uuid.c | 53 + pjlib/src/pj/guid_win32.c | 72 + pjlib/src/pj/hash.c | 349 ++++ pjlib/src/pj/ioqueue_common_abs.c | 1314 +++++++++++++++ pjlib/src/pj/ioqueue_common_abs.h | 139 ++ pjlib/src/pj/ioqueue_dummy.c | 194 +++ pjlib/src/pj/ioqueue_epoll.c | 749 +++++++++ pjlib/src/pj/ioqueue_linux_kernel.c | 162 ++ pjlib/src/pj/ioqueue_select.c | 926 ++++++++++ pjlib/src/pj/ioqueue_symbian.cpp | 853 ++++++++++ pjlib/src/pj/ioqueue_winnt.c | 1443 ++++++++++++++++ pjlib/src/pj/ip_helper_generic.c | 401 +++++ pjlib/src/pj/ip_helper_symbian.cpp | 150 ++ pjlib/src/pj/ip_helper_win32.c | 441 +++++ pjlib/src/pj/list.c | 26 + pjlib/src/pj/lock.c | 198 +++ pjlib/src/pj/log.c | 543 ++++++ pjlib/src/pj/log_writer_printk.c | 28 + pjlib/src/pj/log_writer_stdout.c | 57 + pjlib/src/pj/log_writer_symbian_console.cpp | 44 + pjlib/src/pj/os_core_darwin.m | 100 ++ pjlib/src/pj/os_core_linux_kernel.c | 698 ++++++++ pjlib/src/pj/os_core_symbian.cpp | 1063 ++++++++++++ pjlib/src/pj/os_core_unix.c | 1851 ++++++++++++++++++++ pjlib/src/pj/os_core_win32.c | 1456 ++++++++++++++++ pjlib/src/pj/os_error_linux_kernel.c | 81 + pjlib/src/pj/os_error_symbian.cpp | 172 ++ pjlib/src/pj/os_error_unix.c | 69 + pjlib/src/pj/os_error_win32.c | 220 +++ pjlib/src/pj/os_info.c | 319 ++++ pjlib/src/pj/os_info_iphone.m | 53 + pjlib/src/pj/os_info_symbian.cpp | 190 +++ pjlib/src/pj/os_rwmutex.c | 163 ++ pjlib/src/pj/os_symbian.h | 422 +++++ pjlib/src/pj/os_time_bsd.c | 36 + pjlib/src/pj/os_time_common.c | 86 + pjlib/src/pj/os_time_linux_kernel.c | 66 + pjlib/src/pj/os_time_unix.c | 47 + pjlib/src/pj/os_time_win32.c | 303 ++++ pjlib/src/pj/os_timestamp_common.c | 206 +++ pjlib/src/pj/os_timestamp_linux_kernel.c | 79 + pjlib/src/pj/os_timestamp_posix.c | 220 +++ pjlib/src/pj/os_timestamp_win32.c | 295 ++++ pjlib/src/pj/pool.c | 301 ++++ pjlib/src/pj/pool_buf.c | 115 ++ pjlib/src/pj/pool_caching.c | 334 ++++ pjlib/src/pj/pool_dbg.c | 190 +++ pjlib/src/pj/pool_policy_kmalloc.c | 64 + pjlib/src/pj/pool_policy_malloc.c | 104 ++ pjlib/src/pj/pool_policy_new.cpp | 102 ++ pjlib/src/pj/pool_signature.h | 68 + pjlib/src/pj/rand.c | 35 + pjlib/src/pj/rbtree.c | 428 +++++ pjlib/src/pj/sock_bsd.c | 848 ++++++++++ pjlib/src/pj/sock_common.c | 1196 +++++++++++++ pjlib/src/pj/sock_linux_kernel.c | 755 +++++++++ pjlib/src/pj/sock_qos_bsd.c | 132 ++ pjlib/src/pj/sock_qos_common.c | 151 ++ pjlib/src/pj/sock_qos_dummy.c | 76 + pjlib/src/pj/sock_qos_symbian.cpp | 95 ++ pjlib/src/pj/sock_qos_wm.c | 103 ++ pjlib/src/pj/sock_select.c | 113 ++ pjlib/src/pj/sock_select_symbian.cpp | 163 ++ pjlib/src/pj/sock_symbian.cpp | 1021 +++++++++++ pjlib/src/pj/ssl_sock_common.c | 138 ++ pjlib/src/pj/ssl_sock_dump.c | 148 ++ pjlib/src/pj/ssl_sock_ossl.c | 2414 +++++++++++++++++++++++++++ pjlib/src/pj/ssl_sock_symbian.cpp | 1426 ++++++++++++++++ pjlib/src/pj/string.c | 202 +++ pjlib/src/pj/symbols.c | 348 ++++ pjlib/src/pj/timer.c | 610 +++++++ pjlib/src/pj/timer_symbian.cpp | 446 +++++ pjlib/src/pj/types.c | 46 + pjlib/src/pj/unicode_symbian.cpp | 76 + pjlib/src/pj/unicode_win32.c | 59 + 98 files changed, 32192 insertions(+) create mode 100644 pjlib/src/pj/activesock.c create mode 100644 pjlib/src/pj/addr_resolv_linux_kernel.c create mode 100644 pjlib/src/pj/addr_resolv_sock.c create mode 100644 pjlib/src/pj/addr_resolv_symbian.cpp create mode 100644 pjlib/src/pj/array.c create mode 100644 pjlib/src/pj/compat/longjmp_i386.S create mode 100644 pjlib/src/pj/compat/setjmp_i386.S create mode 100644 pjlib/src/pj/compat/sigjmp.c create mode 100644 pjlib/src/pj/compat/string.c create mode 100644 pjlib/src/pj/compat/string_compat.c create mode 100644 pjlib/src/pj/config.c create mode 100644 pjlib/src/pj/ctype.c create mode 100644 pjlib/src/pj/errno.c create mode 100644 pjlib/src/pj/except.c create mode 100644 pjlib/src/pj/exception_symbian.cpp create mode 100644 pjlib/src/pj/extra-exports.c create mode 100644 pjlib/src/pj/fifobuf.c create mode 100644 pjlib/src/pj/file_access_unistd.c create mode 100644 pjlib/src/pj/file_access_win32.c create mode 100644 pjlib/src/pj/file_io_ansi.c create mode 100644 pjlib/src/pj/file_io_win32.c create mode 100644 pjlib/src/pj/guid.c create mode 100644 pjlib/src/pj/guid_simple.c create mode 100644 pjlib/src/pj/guid_uuid.c create mode 100644 pjlib/src/pj/guid_win32.c create mode 100644 pjlib/src/pj/hash.c create mode 100644 pjlib/src/pj/ioqueue_common_abs.c create mode 100644 pjlib/src/pj/ioqueue_common_abs.h create mode 100644 pjlib/src/pj/ioqueue_dummy.c create mode 100644 pjlib/src/pj/ioqueue_epoll.c create mode 100644 pjlib/src/pj/ioqueue_linux_kernel.c create mode 100644 pjlib/src/pj/ioqueue_select.c create mode 100644 pjlib/src/pj/ioqueue_symbian.cpp create mode 100644 pjlib/src/pj/ioqueue_winnt.c create mode 100644 pjlib/src/pj/ip_helper_generic.c create mode 100644 pjlib/src/pj/ip_helper_symbian.cpp create mode 100644 pjlib/src/pj/ip_helper_win32.c create mode 100644 pjlib/src/pj/list.c create mode 100644 pjlib/src/pj/lock.c create mode 100644 pjlib/src/pj/log.c create mode 100644 pjlib/src/pj/log_writer_printk.c create mode 100644 pjlib/src/pj/log_writer_stdout.c create mode 100644 pjlib/src/pj/log_writer_symbian_console.cpp create mode 100644 pjlib/src/pj/os_core_darwin.m create mode 100644 pjlib/src/pj/os_core_linux_kernel.c create mode 100644 pjlib/src/pj/os_core_symbian.cpp create mode 100644 pjlib/src/pj/os_core_unix.c create mode 100644 pjlib/src/pj/os_core_win32.c create mode 100644 pjlib/src/pj/os_error_linux_kernel.c create mode 100644 pjlib/src/pj/os_error_symbian.cpp create mode 100644 pjlib/src/pj/os_error_unix.c create mode 100644 pjlib/src/pj/os_error_win32.c create mode 100644 pjlib/src/pj/os_info.c create mode 100644 pjlib/src/pj/os_info_iphone.m create mode 100644 pjlib/src/pj/os_info_symbian.cpp create mode 100644 pjlib/src/pj/os_rwmutex.c create mode 100644 pjlib/src/pj/os_symbian.h create mode 100644 pjlib/src/pj/os_time_bsd.c create mode 100644 pjlib/src/pj/os_time_common.c create mode 100644 pjlib/src/pj/os_time_linux_kernel.c create mode 100644 pjlib/src/pj/os_time_unix.c create mode 100644 pjlib/src/pj/os_time_win32.c create mode 100644 pjlib/src/pj/os_timestamp_common.c create mode 100644 pjlib/src/pj/os_timestamp_linux_kernel.c create mode 100644 pjlib/src/pj/os_timestamp_posix.c create mode 100644 pjlib/src/pj/os_timestamp_win32.c create mode 100644 pjlib/src/pj/pool.c create mode 100644 pjlib/src/pj/pool_buf.c create mode 100644 pjlib/src/pj/pool_caching.c create mode 100644 pjlib/src/pj/pool_dbg.c create mode 100644 pjlib/src/pj/pool_policy_kmalloc.c create mode 100644 pjlib/src/pj/pool_policy_malloc.c create mode 100644 pjlib/src/pj/pool_policy_new.cpp create mode 100644 pjlib/src/pj/pool_signature.h create mode 100644 pjlib/src/pj/rand.c create mode 100644 pjlib/src/pj/rbtree.c create mode 100644 pjlib/src/pj/sock_bsd.c create mode 100644 pjlib/src/pj/sock_common.c create mode 100644 pjlib/src/pj/sock_linux_kernel.c create mode 100644 pjlib/src/pj/sock_qos_bsd.c create mode 100644 pjlib/src/pj/sock_qos_common.c create mode 100644 pjlib/src/pj/sock_qos_dummy.c create mode 100644 pjlib/src/pj/sock_qos_symbian.cpp create mode 100644 pjlib/src/pj/sock_qos_wm.c create mode 100644 pjlib/src/pj/sock_select.c create mode 100644 pjlib/src/pj/sock_select_symbian.cpp create mode 100644 pjlib/src/pj/sock_symbian.cpp create mode 100644 pjlib/src/pj/ssl_sock_common.c create mode 100644 pjlib/src/pj/ssl_sock_dump.c create mode 100644 pjlib/src/pj/ssl_sock_ossl.c create mode 100644 pjlib/src/pj/ssl_sock_symbian.cpp create mode 100644 pjlib/src/pj/string.c create mode 100644 pjlib/src/pj/symbols.c create mode 100644 pjlib/src/pj/timer.c create mode 100644 pjlib/src/pj/timer_symbian.cpp create mode 100644 pjlib/src/pj/types.c create mode 100644 pjlib/src/pj/unicode_symbian.cpp create mode 100644 pjlib/src/pj/unicode_win32.c (limited to 'pjlib/src/pj') diff --git a/pjlib/src/pj/activesock.c b/pjlib/src/pj/activesock.c new file mode 100644 index 0000000..5c91383 --- /dev/null +++ b/pjlib/src/pj/activesock.c @@ -0,0 +1,882 @@ +/* $Id: activesock.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 +# include + + static pj_bool_t ios_bg_support = PJ_TRUE; +#endif + +#define PJ_ACTIVESOCK_MAX_LOOP 50 + + +enum read_type +{ + TYPE_NONE, + TYPE_RECV, + TYPE_RECV_FROM +}; + +struct read_op +{ + pj_ioqueue_op_key_t op_key; + pj_uint8_t *pkt; + unsigned max_size; + pj_size_t size; + pj_sockaddr src_addr; + int src_addr_len; +}; + +struct accept_op +{ + pj_ioqueue_op_key_t op_key; + pj_sock_t new_sock; + pj_sockaddr rem_addr; + int rem_addr_len; +}; + +struct send_data +{ + pj_uint8_t *data; + pj_ssize_t len; + pj_ssize_t sent; + unsigned flags; +}; + +struct pj_activesock_t +{ + pj_ioqueue_key_t *key; + pj_bool_t stream_oriented; + pj_bool_t whole_data; + pj_ioqueue_t *ioqueue; + void *user_data; + unsigned async_count; + unsigned max_loop; + pj_activesock_cb cb; +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 + int bg_setting; + pj_sock_t sock; + CFReadStreamRef readStream; +#endif + + unsigned err_counter; + pj_status_t last_err; + + struct send_data send_data; + + struct read_op *read_op; + pj_uint32_t read_flags; + enum read_type read_type; + + struct accept_op *accept_op; +}; + + +static void ioqueue_on_read_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_read); +static void ioqueue_on_write_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_sent); +#if PJ_HAS_TCP +static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_sock_t sock, + pj_status_t status); +static void ioqueue_on_connect_complete(pj_ioqueue_key_t *key, + pj_status_t status); +#endif + +PJ_DEF(void) pj_activesock_cfg_default(pj_activesock_cfg *cfg) +{ + pj_bzero(cfg, sizeof(*cfg)); + cfg->async_cnt = 1; + cfg->concurrency = -1; + cfg->whole_data = PJ_TRUE; +} + +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 +static void activesock_destroy_iphone_os_stream(pj_activesock_t *asock) +{ + if (asock->readStream) { + CFReadStreamClose(asock->readStream); + CFRelease(asock->readStream); + asock->readStream = NULL; + } +} + +static void activesock_create_iphone_os_stream(pj_activesock_t *asock) +{ + if (ios_bg_support && asock->bg_setting && asock->stream_oriented) { + activesock_destroy_iphone_os_stream(asock); + + CFStreamCreatePairWithSocket(kCFAllocatorDefault, asock->sock, + &asock->readStream, NULL); + + if (!asock->readStream || + CFReadStreamSetProperty(asock->readStream, + kCFStreamNetworkServiceType, + kCFStreamNetworkServiceTypeVoIP) + != TRUE || + CFReadStreamOpen(asock->readStream) != TRUE) + { + PJ_LOG(2,("", "Failed to configure TCP transport for VoIP " + "usage. Background mode will not be supported.")); + + activesock_destroy_iphone_os_stream(asock); + } + } +} + + +PJ_DEF(void) pj_activesock_set_iphone_os_bg(pj_activesock_t *asock, + int val) +{ + asock->bg_setting = val; + if (asock->bg_setting) + activesock_create_iphone_os_stream(asock); + else + activesock_destroy_iphone_os_stream(asock); +} + +PJ_DEF(void) pj_activesock_enable_iphone_os_bg(pj_bool_t val) +{ + ios_bg_support = val; +} +#endif + +PJ_DEF(pj_status_t) pj_activesock_create( pj_pool_t *pool, + pj_sock_t sock, + int sock_type, + const pj_activesock_cfg *opt, + pj_ioqueue_t *ioqueue, + const pj_activesock_cb *cb, + void *user_data, + pj_activesock_t **p_asock) +{ + pj_activesock_t *asock; + pj_ioqueue_callback ioq_cb; + pj_status_t status; + + PJ_ASSERT_RETURN(pool && ioqueue && cb && p_asock, PJ_EINVAL); + PJ_ASSERT_RETURN(sock!=0 && sock!=PJ_INVALID_SOCKET, PJ_EINVAL); + PJ_ASSERT_RETURN(sock_type==pj_SOCK_STREAM() || + sock_type==pj_SOCK_DGRAM(), PJ_EINVAL); + PJ_ASSERT_RETURN(!opt || opt->async_cnt >= 1, PJ_EINVAL); + + asock = PJ_POOL_ZALLOC_T(pool, pj_activesock_t); + asock->ioqueue = ioqueue; + asock->stream_oriented = (sock_type == pj_SOCK_STREAM()); + asock->async_count = (opt? opt->async_cnt : 1); + asock->whole_data = (opt? opt->whole_data : 1); + asock->max_loop = PJ_ACTIVESOCK_MAX_LOOP; + asock->user_data = user_data; + pj_memcpy(&asock->cb, cb, sizeof(*cb)); + + pj_bzero(&ioq_cb, sizeof(ioq_cb)); + ioq_cb.on_read_complete = &ioqueue_on_read_complete; + ioq_cb.on_write_complete = &ioqueue_on_write_complete; +#if PJ_HAS_TCP + ioq_cb.on_connect_complete = &ioqueue_on_connect_complete; + ioq_cb.on_accept_complete = &ioqueue_on_accept_complete; +#endif + + status = pj_ioqueue_register_sock(pool, ioqueue, sock, asock, + &ioq_cb, &asock->key); + if (status != PJ_SUCCESS) { + pj_activesock_close(asock); + return status; + } + + if (asock->whole_data) { + /* Must disable concurrency otherwise there is a race condition */ + pj_ioqueue_set_concurrency(asock->key, 0); + } else if (opt && opt->concurrency >= 0) { + pj_ioqueue_set_concurrency(asock->key, opt->concurrency); + } + +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 + asock->sock = sock; + asock->bg_setting = PJ_ACTIVESOCK_TCP_IPHONE_OS_BG; +#endif + + *p_asock = asock; + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_activesock_create_udp( pj_pool_t *pool, + const pj_sockaddr *addr, + const pj_activesock_cfg *opt, + pj_ioqueue_t *ioqueue, + const pj_activesock_cb *cb, + void *user_data, + pj_activesock_t **p_asock, + pj_sockaddr *bound_addr) +{ + pj_sock_t sock_fd; + pj_sockaddr default_addr; + pj_status_t status; + + if (addr == NULL) { + pj_sockaddr_init(pj_AF_INET(), &default_addr, NULL, 0); + addr = &default_addr; + } + + status = pj_sock_socket(addr->addr.sa_family, pj_SOCK_DGRAM(), 0, + &sock_fd); + if (status != PJ_SUCCESS) { + return status; + } + + status = pj_sock_bind(sock_fd, addr, pj_sockaddr_get_len(addr)); + if (status != PJ_SUCCESS) { + pj_sock_close(sock_fd); + return status; + } + + status = pj_activesock_create(pool, sock_fd, pj_SOCK_DGRAM(), opt, + ioqueue, cb, user_data, p_asock); + if (status != PJ_SUCCESS) { + pj_sock_close(sock_fd); + return status; + } + + if (bound_addr) { + int addr_len = sizeof(*bound_addr); + status = pj_sock_getsockname(sock_fd, bound_addr, &addr_len); + if (status != PJ_SUCCESS) { + pj_activesock_close(*p_asock); + return status; + } + } + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_activesock_close(pj_activesock_t *asock) +{ + PJ_ASSERT_RETURN(asock, PJ_EINVAL); + if (asock->key) { +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 + activesock_destroy_iphone_os_stream(asock); +#endif + + pj_ioqueue_unregister(asock->key); + asock->key = NULL; + } + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_activesock_set_user_data( pj_activesock_t *asock, + void *user_data) +{ + PJ_ASSERT_RETURN(asock, PJ_EINVAL); + asock->user_data = user_data; + return PJ_SUCCESS; +} + + +PJ_DEF(void*) pj_activesock_get_user_data(pj_activesock_t *asock) +{ + PJ_ASSERT_RETURN(asock, NULL); + return asock->user_data; +} + + +PJ_DEF(pj_status_t) pj_activesock_start_read(pj_activesock_t *asock, + pj_pool_t *pool, + unsigned buff_size, + pj_uint32_t flags) +{ + void **readbuf; + unsigned i; + + PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL); + + readbuf = (void**) pj_pool_calloc(pool, asock->async_count, + sizeof(void*)); + + for (i=0; iasync_count; ++i) { + readbuf[i] = pj_pool_alloc(pool, buff_size); + } + + return pj_activesock_start_read2(asock, pool, buff_size, readbuf, flags); +} + + +PJ_DEF(pj_status_t) pj_activesock_start_read2( pj_activesock_t *asock, + pj_pool_t *pool, + unsigned buff_size, + void *readbuf[], + pj_uint32_t flags) +{ + unsigned i; + pj_status_t status; + + PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL); + PJ_ASSERT_RETURN(asock->read_type == TYPE_NONE, PJ_EINVALIDOP); + PJ_ASSERT_RETURN(asock->read_op == NULL, PJ_EINVALIDOP); + + asock->read_op = (struct read_op*) + pj_pool_calloc(pool, asock->async_count, + sizeof(struct read_op)); + asock->read_type = TYPE_RECV; + asock->read_flags = flags; + + for (i=0; iasync_count; ++i) { + struct read_op *r = &asock->read_op[i]; + pj_ssize_t size_to_read; + + r->pkt = (pj_uint8_t*)readbuf[i]; + r->max_size = size_to_read = buff_size; + + status = pj_ioqueue_recv(asock->key, &r->op_key, r->pkt, &size_to_read, + PJ_IOQUEUE_ALWAYS_ASYNC | flags); + PJ_ASSERT_RETURN(status != PJ_SUCCESS, PJ_EBUG); + + if (status != PJ_EPENDING) + return status; + } + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_activesock_start_recvfrom(pj_activesock_t *asock, + pj_pool_t *pool, + unsigned buff_size, + pj_uint32_t flags) +{ + void **readbuf; + unsigned i; + + PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL); + + readbuf = (void**) pj_pool_calloc(pool, asock->async_count, + sizeof(void*)); + + for (i=0; iasync_count; ++i) { + readbuf[i] = pj_pool_alloc(pool, buff_size); + } + + return pj_activesock_start_recvfrom2(asock, pool, buff_size, + readbuf, flags); +} + + +PJ_DEF(pj_status_t) pj_activesock_start_recvfrom2( pj_activesock_t *asock, + pj_pool_t *pool, + unsigned buff_size, + void *readbuf[], + pj_uint32_t flags) +{ + unsigned i; + pj_status_t status; + + PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL); + PJ_ASSERT_RETURN(asock->read_type == TYPE_NONE, PJ_EINVALIDOP); + + asock->read_op = (struct read_op*) + pj_pool_calloc(pool, asock->async_count, + sizeof(struct read_op)); + asock->read_type = TYPE_RECV_FROM; + asock->read_flags = flags; + + for (i=0; iasync_count; ++i) { + struct read_op *r = &asock->read_op[i]; + pj_ssize_t size_to_read; + + r->pkt = (pj_uint8_t*) readbuf[i]; + r->max_size = size_to_read = buff_size; + r->src_addr_len = sizeof(r->src_addr); + + status = pj_ioqueue_recvfrom(asock->key, &r->op_key, r->pkt, + &size_to_read, + PJ_IOQUEUE_ALWAYS_ASYNC | flags, + &r->src_addr, &r->src_addr_len); + PJ_ASSERT_RETURN(status != PJ_SUCCESS, PJ_EBUG); + + if (status != PJ_EPENDING) + return status; + } + + return PJ_SUCCESS; +} + + +static void ioqueue_on_read_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_read) +{ + pj_activesock_t *asock; + struct read_op *r = (struct read_op*)op_key; + unsigned loop = 0; + pj_status_t status; + + asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key); + + do { + unsigned flags; + + if (bytes_read > 0) { + /* + * We've got new data. + */ + pj_size_t remainder; + pj_bool_t ret; + + /* Append this new data to existing data. If socket is stream + * oriented, user might have left some data in the buffer. + * Otherwise if socket is datagram there will be nothing in + * existing packet hence the packet will contain only the new + * packet. + */ + r->size += bytes_read; + + /* Set default remainder to zero */ + remainder = 0; + + /* And return value to TRUE */ + ret = PJ_TRUE; + + /* Notify callback */ + if (asock->read_type == TYPE_RECV && asock->cb.on_data_read) { + ret = (*asock->cb.on_data_read)(asock, r->pkt, r->size, + PJ_SUCCESS, &remainder); + } else if (asock->read_type == TYPE_RECV_FROM && + asock->cb.on_data_recvfrom) + { + ret = (*asock->cb.on_data_recvfrom)(asock, r->pkt, r->size, + &r->src_addr, + r->src_addr_len, + PJ_SUCCESS); + } + + /* If callback returns false, we have been destroyed! */ + if (!ret) + return; + + /* Only stream oriented socket may leave data in the packet */ + if (asock->stream_oriented) { + r->size = remainder; + } else { + r->size = 0; + } + + } else if (bytes_read <= 0 && + -bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) && + -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && + (asock->stream_oriented || + -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET))) + { + pj_size_t remainder; + pj_bool_t ret; + + if (bytes_read == 0) { + /* For stream/connection oriented socket, this means the + * connection has been closed. For datagram sockets, it means + * we've received datagram with zero length. + */ + if (asock->stream_oriented) + status = PJ_EEOF; + else + status = PJ_SUCCESS; + } else { + /* This means we've got an error. If this is stream/connection + * oriented, it means connection has been closed. For datagram + * sockets, it means we've got some error (e.g. EWOULDBLOCK). + */ + status = -bytes_read; + } + + /* Set default remainder to zero */ + remainder = 0; + + /* And return value to TRUE */ + ret = PJ_TRUE; + + /* Notify callback */ + if (asock->read_type == TYPE_RECV && asock->cb.on_data_read) { + /* For connection oriented socket, we still need to report + * the remainder data (if any) to the user to let user do + * processing with the remainder data before it closes the + * connection. + * If there is no remainder data, set the packet to NULL. + */ + + /* Shouldn't set the packet to NULL, as there may be active + * socket user, such as SSL socket, that needs to have access + * to the read buffer packet. + */ + //ret = (*asock->cb.on_data_read)(asock, (r->size? r->pkt:NULL), + // r->size, status, &remainder); + ret = (*asock->cb.on_data_read)(asock, r->pkt, r->size, + status, &remainder); + + } else if (asock->read_type == TYPE_RECV_FROM && + asock->cb.on_data_recvfrom) + { + /* This would always be datagram oriented hence there's + * nothing in the packet. We can't be sure if there will be + * anything useful in the source_addr, so just put NULL + * there too. + */ + /* In some scenarios, status may be PJ_SUCCESS. The upper + * layer application may not expect the callback to be called + * with successful status and NULL data, so lets not call the + * callback if the status is PJ_SUCCESS. + */ + if (status != PJ_SUCCESS ) { + ret = (*asock->cb.on_data_recvfrom)(asock, NULL, 0, + NULL, 0, status); + } + } + + /* If callback returns false, we have been destroyed! */ + if (!ret) + return; + + /* Only stream oriented socket may leave data in the packet */ + if (asock->stream_oriented) { + r->size = remainder; + } else { + r->size = 0; + } + } + + /* Read next data. We limit ourselves to processing max_loop immediate + * data, so when the loop counter has exceeded this value, force the + * read()/recvfrom() to return pending operation to allow the program + * to do other jobs. + */ + bytes_read = r->max_size - r->size; + flags = asock->read_flags; + if (++loop >= asock->max_loop) + flags |= PJ_IOQUEUE_ALWAYS_ASYNC; + + if (asock->read_type == TYPE_RECV) { + status = pj_ioqueue_recv(key, op_key, r->pkt + r->size, + &bytes_read, flags); + } else { + r->src_addr_len = sizeof(r->src_addr); + status = pj_ioqueue_recvfrom(key, op_key, r->pkt + r->size, + &bytes_read, flags, + &r->src_addr, &r->src_addr_len); + } + + if (status == PJ_SUCCESS) { + /* Immediate data */ + ; + } else if (status != PJ_EPENDING && status != PJ_ECANCELLED) { + /* Error */ + bytes_read = -status; + } else { + break; + } + } while (1); + +} + + +static pj_status_t send_remaining(pj_activesock_t *asock, + pj_ioqueue_op_key_t *send_key) +{ + struct send_data *sd = (struct send_data*)send_key->activesock_data; + pj_status_t status; + + do { + pj_ssize_t size; + + size = sd->len - sd->sent; + status = pj_ioqueue_send(asock->key, send_key, + sd->data+sd->sent, &size, sd->flags); + if (status != PJ_SUCCESS) { + /* Pending or error */ + break; + } + + sd->sent += size; + if (sd->sent == sd->len) { + /* The whole data has been sent. */ + return PJ_SUCCESS; + } + + } while (sd->sent < sd->len); + + return status; +} + + +PJ_DEF(pj_status_t) pj_activesock_send( pj_activesock_t *asock, + pj_ioqueue_op_key_t *send_key, + const void *data, + pj_ssize_t *size, + unsigned flags) +{ + PJ_ASSERT_RETURN(asock && send_key && data && size, PJ_EINVAL); + + send_key->activesock_data = NULL; + + if (asock->whole_data) { + pj_ssize_t whole; + pj_status_t status; + + whole = *size; + + status = pj_ioqueue_send(asock->key, send_key, data, size, flags); + if (status != PJ_SUCCESS) { + /* Pending or error */ + return status; + } + + if (*size == whole) { + /* The whole data has been sent. */ + return PJ_SUCCESS; + } + + /* Data was partially sent */ + asock->send_data.data = (pj_uint8_t*)data; + asock->send_data.len = whole; + asock->send_data.sent = *size; + asock->send_data.flags = flags; + send_key->activesock_data = &asock->send_data; + + /* Try again */ + status = send_remaining(asock, send_key); + if (status == PJ_SUCCESS) { + *size = whole; + } + return status; + + } else { + return pj_ioqueue_send(asock->key, send_key, data, size, flags); + } +} + + +PJ_DEF(pj_status_t) pj_activesock_sendto( pj_activesock_t *asock, + pj_ioqueue_op_key_t *send_key, + const void *data, + pj_ssize_t *size, + unsigned flags, + const pj_sockaddr_t *addr, + int addr_len) +{ + PJ_ASSERT_RETURN(asock && send_key && data && size && addr && addr_len, + PJ_EINVAL); + + return pj_ioqueue_sendto(asock->key, send_key, data, size, flags, + addr, addr_len); +} + + +static void ioqueue_on_write_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_sent) +{ + pj_activesock_t *asock; + + asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key); + + if (bytes_sent > 0 && op_key->activesock_data) { + /* whole_data is requested. Make sure we send all the data */ + struct send_data *sd = (struct send_data*)op_key->activesock_data; + + sd->sent += bytes_sent; + if (sd->sent == sd->len) { + /* all has been sent */ + bytes_sent = sd->sent; + op_key->activesock_data = NULL; + } else { + /* send remaining data */ + pj_status_t status; + + status = send_remaining(asock, op_key); + if (status == PJ_EPENDING) + return; + else if (status == PJ_SUCCESS) + bytes_sent = sd->sent; + else + bytes_sent = -status; + + op_key->activesock_data = NULL; + } + } + + if (asock->cb.on_data_sent) { + pj_bool_t ret; + + ret = (*asock->cb.on_data_sent)(asock, op_key, bytes_sent); + + /* If callback returns false, we have been destroyed! */ + if (!ret) + return; + } +} + +#if PJ_HAS_TCP +PJ_DEF(pj_status_t) pj_activesock_start_accept(pj_activesock_t *asock, + pj_pool_t *pool) +{ + unsigned i; + + PJ_ASSERT_RETURN(asock, PJ_EINVAL); + PJ_ASSERT_RETURN(asock->accept_op==NULL, PJ_EINVALIDOP); + + asock->accept_op = (struct accept_op*) + pj_pool_calloc(pool, asock->async_count, + sizeof(struct accept_op)); + for (i=0; iasync_count; ++i) { + struct accept_op *a = &asock->accept_op[i]; + pj_status_t status; + + do { + a->new_sock = PJ_INVALID_SOCKET; + a->rem_addr_len = sizeof(a->rem_addr); + + status = pj_ioqueue_accept(asock->key, &a->op_key, &a->new_sock, + NULL, &a->rem_addr, &a->rem_addr_len); + if (status == PJ_SUCCESS) { + /* We've got immediate connection. Not sure if it's a good + * idea to call the callback now (probably application will + * not be prepared to process it), so lets just silently + * close the socket. + */ + pj_sock_close(a->new_sock); + } + } while (status == PJ_SUCCESS); + + if (status != PJ_EPENDING) { + return status; + } + } + + return PJ_SUCCESS; +} + + +static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_sock_t new_sock, + pj_status_t status) +{ + pj_activesock_t *asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key); + struct accept_op *accept_op = (struct accept_op*) op_key; + + PJ_UNUSED_ARG(new_sock); + + do { + if (status == asock->last_err && status != PJ_SUCCESS) { + asock->err_counter++; + if (asock->err_counter >= PJ_ACTIVESOCK_MAX_CONSECUTIVE_ACCEPT_ERROR) { + PJ_LOG(3, ("", "Received %d consecutive errors: %d for the accept()" + " operation, stopping further ioqueue accepts.", + asock->err_counter, asock->last_err)); + return; + } + } else { + asock->err_counter = 0; + asock->last_err = status; + } + + if (status==PJ_SUCCESS && asock->cb.on_accept_complete) { + pj_bool_t ret; + + /* Notify callback */ + ret = (*asock->cb.on_accept_complete)(asock, accept_op->new_sock, + &accept_op->rem_addr, + accept_op->rem_addr_len); + + /* If callback returns false, we have been destroyed! */ + if (!ret) + return; + +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 + activesock_create_iphone_os_stream(asock); +#endif + } else if (status==PJ_SUCCESS) { + /* Application doesn't handle the new socket, we need to + * close it to avoid resource leak. + */ + pj_sock_close(accept_op->new_sock); + } + + /* Prepare next accept() */ + accept_op->new_sock = PJ_INVALID_SOCKET; + accept_op->rem_addr_len = sizeof(accept_op->rem_addr); + + status = pj_ioqueue_accept(asock->key, op_key, &accept_op->new_sock, + NULL, &accept_op->rem_addr, + &accept_op->rem_addr_len); + + } while (status != PJ_EPENDING && status != PJ_ECANCELLED); +} + + +PJ_DEF(pj_status_t) pj_activesock_start_connect( pj_activesock_t *asock, + pj_pool_t *pool, + const pj_sockaddr_t *remaddr, + int addr_len) +{ + PJ_UNUSED_ARG(pool); + return pj_ioqueue_connect(asock->key, remaddr, addr_len); +} + +static void ioqueue_on_connect_complete(pj_ioqueue_key_t *key, + pj_status_t status) +{ + pj_activesock_t *asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key); + + if (asock->cb.on_connect_complete) { + pj_bool_t ret; + + ret = (*asock->cb.on_connect_complete)(asock, status); + + if (!ret) { + /* We've been destroyed */ + return; + } + +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 + activesock_create_iphone_os_stream(asock); +#endif + + } +} +#endif /* PJ_HAS_TCP */ + diff --git a/pjlib/src/pj/addr_resolv_linux_kernel.c b/pjlib/src/pj/addr_resolv_linux_kernel.c new file mode 100644 index 0000000..c6de670 --- /dev/null +++ b/pjlib/src/pj/addr_resolv_linux_kernel.c @@ -0,0 +1,26 @@ +/* $Id: addr_resolv_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + +PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe) +{ + return -1; +} + diff --git a/pjlib/src/pj/addr_resolv_sock.c b/pjlib/src/pj/addr_resolv_sock.c new file mode 100644 index 0000000..84f7ed8 --- /dev/null +++ b/pjlib/src/pj/addr_resolv_sock.c @@ -0,0 +1,282 @@ +/* $Id: addr_resolv_sock.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include + +#if defined(PJ_GETADDRINFO_USE_CFHOST) && PJ_GETADDRINFO_USE_CFHOST!=0 +# include +# include +#endif + +PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe) +{ + struct hostent *he; + char copy[PJ_MAX_HOSTNAME]; + + pj_assert(hostname && hostname ->slen < PJ_MAX_HOSTNAME); + + if (hostname->slen >= PJ_MAX_HOSTNAME) + return PJ_ENAMETOOLONG; + + pj_memcpy(copy, hostname->ptr, hostname->slen); + copy[ hostname->slen ] = '\0'; + + he = gethostbyname(copy); + if (!he) { + return PJ_ERESOLVE; + /* DO NOT use pj_get_netos_error() since host resolution error + * is reported in h_errno instead of errno! + return pj_get_netos_error(); + */ + } + + phe->h_name = he->h_name; + phe->h_aliases = he->h_aliases; + phe->h_addrtype = he->h_addrtype; + phe->h_length = he->h_length; + phe->h_addr_list = he->h_addr_list; + + return PJ_SUCCESS; +} + +/* Resolve IPv4/IPv6 address */ +PJ_DEF(pj_status_t) pj_getaddrinfo(int af, const pj_str_t *nodename, + unsigned *count, pj_addrinfo ai[]) +{ +#if defined(PJ_SOCK_HAS_GETADDRINFO) && PJ_SOCK_HAS_GETADDRINFO!=0 + char nodecopy[PJ_MAX_HOSTNAME]; + pj_bool_t has_addr = PJ_FALSE; + unsigned i; +#if defined(PJ_GETADDRINFO_USE_CFHOST) && PJ_GETADDRINFO_USE_CFHOST!=0 + CFStringRef hostname; + CFHostRef hostRef; + pj_status_t status = PJ_SUCCESS; +#else + int rc; + struct addrinfo hint, *res, *orig_res; +#endif + + PJ_ASSERT_RETURN(nodename && count && *count && ai, PJ_EINVAL); + PJ_ASSERT_RETURN(nodename->ptr && nodename->slen, PJ_EINVAL); + PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6 || + af==PJ_AF_UNSPEC, PJ_EINVAL); + + /* Check if nodename is IP address */ + pj_bzero(&ai[0], sizeof(ai[0])); + if ((af==PJ_AF_INET || af==PJ_AF_UNSPEC) && + pj_inet_pton(PJ_AF_INET, nodename, + &ai[0].ai_addr.ipv4.sin_addr) == PJ_SUCCESS) + { + af = PJ_AF_INET; + has_addr = PJ_TRUE; + } else if ((af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) && + pj_inet_pton(PJ_AF_INET6, nodename, + &ai[0].ai_addr.ipv6.sin6_addr) == PJ_SUCCESS) + { + af = PJ_AF_INET6; + has_addr = PJ_TRUE; + } + + if (has_addr) { + pj_str_t tmp; + + tmp.ptr = ai[0].ai_canonname; + pj_strncpy_with_null(&tmp, nodename, PJ_MAX_HOSTNAME); + ai[0].ai_addr.addr.sa_family = (pj_uint16_t)af; + *count = 1; + + return PJ_SUCCESS; + } + + /* Copy node name to null terminated string. */ + if (nodename->slen >= PJ_MAX_HOSTNAME) + return PJ_ENAMETOOLONG; + pj_memcpy(nodecopy, nodename->ptr, nodename->slen); + nodecopy[nodename->slen] = '\0'; + +#if defined(PJ_GETADDRINFO_USE_CFHOST) && PJ_GETADDRINFO_USE_CFHOST!=0 + hostname = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, nodecopy, + kCFStringEncodingASCII, + kCFAllocatorNull); + hostRef = CFHostCreateWithName(kCFAllocatorDefault, hostname); + if (CFHostStartInfoResolution(hostRef, kCFHostAddresses, nil)) { + CFArrayRef addrRef = CFHostGetAddressing(hostRef, nil); + i = 0; + if (addrRef != nil) { + CFIndex idx, naddr; + + naddr = CFArrayGetCount(addrRef); + for (idx = 0; idx < naddr && i < *count; idx++) { + struct sockaddr *addr; + + addr = (struct sockaddr *) + CFDataGetBytePtr(CFArrayGetValueAtIndex(addrRef, idx)); + /* This should not happen. */ + pj_assert(addr); + + /* Ignore unwanted address families */ + if (af!=PJ_AF_UNSPEC && addr->sa_family != af) + continue; + + /* Store canonical name */ + pj_ansi_strcpy(ai[i].ai_canonname, nodecopy); + + /* Store address */ + PJ_ASSERT_ON_FAIL(sizeof(*addr) <= sizeof(pj_sockaddr), + continue); + pj_memcpy(&ai[i].ai_addr, addr, sizeof(*addr)); + PJ_SOCKADDR_RESET_LEN(&ai[i].ai_addr); + + i++; + } + } + + *count = i; + } else { + status = PJ_ERESOLVE; + } + + CFRelease(hostRef); + CFRelease(hostname); + + return status; +#else + /* Call getaddrinfo() */ + pj_bzero(&hint, sizeof(hint)); + hint.ai_family = af; + + rc = getaddrinfo(nodecopy, NULL, &hint, &res); + if (rc != 0) + return PJ_ERESOLVE; + + orig_res = res; + + /* Enumerate each item in the result */ + for (i=0; i<*count && res; res=res->ai_next) { + /* Ignore unwanted address families */ + if (af!=PJ_AF_UNSPEC && res->ai_family != af) + continue; + + /* Store canonical name (possibly truncating the name) */ + if (res->ai_canonname) { + pj_ansi_strncpy(ai[i].ai_canonname, res->ai_canonname, + sizeof(ai[i].ai_canonname)); + ai[i].ai_canonname[sizeof(ai[i].ai_canonname)-1] = '\0'; + } else { + pj_ansi_strcpy(ai[i].ai_canonname, nodecopy); + } + + /* Store address */ + PJ_ASSERT_ON_FAIL(res->ai_addrlen <= sizeof(pj_sockaddr), continue); + pj_memcpy(&ai[i].ai_addr, res->ai_addr, res->ai_addrlen); + PJ_SOCKADDR_RESET_LEN(&ai[i].ai_addr); + + /* Next slot */ + ++i; + } + + *count = i; + + freeaddrinfo(orig_res); + + /* Done */ + return PJ_SUCCESS; +#endif + +#else /* PJ_SOCK_HAS_GETADDRINFO */ + pj_bool_t has_addr = PJ_FALSE; + + PJ_ASSERT_RETURN(count && *count, PJ_EINVAL); + + /* Check if nodename is IP address */ + pj_bzero(&ai[0], sizeof(ai[0])); + if ((af==PJ_AF_INET || af==PJ_AF_UNSPEC) && + pj_inet_pton(PJ_AF_INET, nodename, + &ai[0].ai_addr.ipv4.sin_addr) == PJ_SUCCESS) + { + af = PJ_AF_INET; + has_addr = PJ_TRUE; + } + else if ((af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) && + pj_inet_pton(PJ_AF_INET6, nodename, + &ai[0].ai_addr.ipv6.sin6_addr) == PJ_SUCCESS) + { + af = PJ_AF_INET6; + has_addr = PJ_TRUE; + } + + if (has_addr) { + pj_str_t tmp; + + tmp.ptr = ai[0].ai_canonname; + pj_strncpy_with_null(&tmp, nodename, PJ_MAX_HOSTNAME); + ai[0].ai_addr.addr.sa_family = (pj_uint16_t)af; + *count = 1; + + return PJ_SUCCESS; + } + + if (af == PJ_AF_INET || af == PJ_AF_UNSPEC) { + pj_hostent he; + unsigned i, max_count; + pj_status_t status; + + /* VC6 complains that "he" is uninitialized */ + #ifdef _MSC_VER + pj_bzero(&he, sizeof(he)); + #endif + + status = pj_gethostbyname(nodename, &he); + if (status != PJ_SUCCESS) + return status; + + max_count = *count; + *count = 0; + + pj_bzero(ai, max_count * sizeof(pj_addrinfo)); + + for (i=0; he.h_addr_list[i] && *count + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "os_symbian.h" + +#define THIS_FILE "addr_resolv_symbian.cpp" +#define TRACE_ME 0 + + +// PJLIB API: resolve hostname +PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *name, pj_hostent *he) +{ + static pj_addrinfo ai; + static char *aliases[2]; + static char *addrlist[2]; + unsigned count = 1; + pj_status_t status; + + status = pj_getaddrinfo(PJ_AF_INET, name, &count, &ai); + if (status != PJ_SUCCESS) + return status; + + aliases[0] = ai.ai_canonname; + aliases[1] = NULL; + + addrlist[0] = (char*) &ai.ai_addr.ipv4.sin_addr; + addrlist[1] = NULL; + + pj_bzero(he, sizeof(*he)); + he->h_name = aliases[0]; + he->h_aliases = aliases; + he->h_addrtype = PJ_AF_INET; + he->h_length = 4; + he->h_addr_list = addrlist; + + return PJ_SUCCESS; +} + + +// Resolve for specific address family +static pj_status_t getaddrinfo_by_af(int af, const pj_str_t *name, + unsigned *count, pj_addrinfo ai[]) +{ + unsigned i; + pj_status_t status; + + PJ_ASSERT_RETURN(name && count && ai, PJ_EINVAL); + +#if !defined(PJ_HAS_IPV6) || !PJ_HAS_IPV6 + if (af == PJ_AF_INET6) + return PJ_EIPV6NOTSUP; +#endif + + // Return failure if access point is marked as down by app. + PJ_SYMBIAN_CHECK_CONNECTION(); + + // Get resolver for the specified address family + RHostResolver &resv = PjSymbianOS::Instance()->GetResolver(af); + + // Convert name to Unicode + wchar_t name16[PJ_MAX_HOSTNAME]; + pj_ansi_to_unicode(name->ptr, name->slen, name16, PJ_ARRAY_SIZE(name16)); + TPtrC16 data((const TUint16*)name16); + + // Resolve! + TNameEntry nameEntry; + TRequestStatus reqStatus; + + resv.GetByName(data, nameEntry, reqStatus); + User::WaitForRequest(reqStatus); + + // Iterate each result + i = 0; + while (reqStatus == KErrNone && i < *count) { + + // Get the resolved TInetAddr + TInetAddr inetAddr(nameEntry().iAddr); + int addrlen; + +#if TRACE_ME + if (1) { + pj_sockaddr a; + char ipaddr[PJ_INET6_ADDRSTRLEN+2]; + int namelen; + + namelen = sizeof(pj_sockaddr); + if (PjSymbianOS::Addr2pj(inetAddr, a, &namelen, + PJ_FALSE) == PJ_SUCCESS) + { + PJ_LOG(5,(THIS_FILE, "resolve %.*s: %s", + (int)name->slen, name->ptr, + pj_sockaddr_print(&a, ipaddr, sizeof(ipaddr), 2))); + } + } +#endif + + // Ignore if this is not the same address family + // Not a good idea, as Symbian mapps IPv4 to IPv6. + //fam = inetAddr.Family(); + //if (fam != af) { + // resv.Next(nameEntry, reqStatus); + // User::WaitForRequest(reqStatus); + // continue; + //} + + // Convert IP address first to get IPv4 mapped address + addrlen = sizeof(ai[i].ai_addr); + status = PjSymbianOS::Addr2pj(inetAddr, ai[i].ai_addr, + &addrlen, PJ_TRUE); + if (status != PJ_SUCCESS) + return status; + + // Ignore if address family doesn't match + if (ai[i].ai_addr.addr.sa_family != af) { + resv.Next(nameEntry, reqStatus); + User::WaitForRequest(reqStatus); + continue; + } + + // Convert the official address to ANSI. + pj_unicode_to_ansi((const wchar_t*)nameEntry().iName.Ptr(), + nameEntry().iName.Length(), + ai[i].ai_canonname, sizeof(ai[i].ai_canonname)); + + // Next + ++i; + resv.Next(nameEntry, reqStatus); + User::WaitForRequest(reqStatus); + } + + *count = i; + return PJ_SUCCESS; +} + +/* Resolve IPv4/IPv6 address */ +PJ_DEF(pj_status_t) pj_getaddrinfo(int af, const pj_str_t *nodename, + unsigned *count, pj_addrinfo ai[]) +{ + unsigned start; + pj_status_t status = PJ_EAFNOTSUP; + + PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6 || af==PJ_AF_UNSPEC, + PJ_EAFNOTSUP); + PJ_ASSERT_RETURN(nodename && count && *count && ai, PJ_EINVAL); + + start = 0; + + if (af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) { + unsigned max = *count; + status = getaddrinfo_by_af(PJ_AF_INET6, nodename, + &max, &ai[start]); + if (status == PJ_SUCCESS) { + (*count) -= max; + start += max; + } + } + + if (af==PJ_AF_INET || af==PJ_AF_UNSPEC) { + unsigned max = *count; + status = getaddrinfo_by_af(PJ_AF_INET, nodename, + &max, &ai[start]); + if (status == PJ_SUCCESS) { + (*count) -= max; + start += max; + } + } + + *count = start; + + if (*count) { + return PJ_SUCCESS; + } else { + return status!=PJ_SUCCESS ? status : PJ_ENOTFOUND; + } +} + diff --git a/pjlib/src/pj/array.c b/pjlib/src/pj/array.c new file mode 100644 index 0000000..5b39d19 --- /dev/null +++ b/pjlib/src/pj/array.c @@ -0,0 +1,71 @@ +/* $Id: array.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include + +PJ_DEF(void) pj_array_insert( void *array, + unsigned elem_size, + unsigned count, + unsigned pos, + const void *value) +{ + if (count && pos < count) { + pj_memmove( (char*)array + (pos+1)*elem_size, + (char*)array + pos*elem_size, + (count-pos)*elem_size); + } + pj_memmove((char*)array + pos*elem_size, value, elem_size); +} + +PJ_DEF(void) pj_array_erase( void *array, + unsigned elem_size, + unsigned count, + unsigned pos) +{ + pj_assert(count != 0); + if (pos < count-1) { + pj_memmove( (char*)array + pos*elem_size, + (char*)array + (pos+1)*elem_size, + (count-pos-1)*elem_size); + } +} + +PJ_DEF(pj_status_t) pj_array_find( const void *array, + unsigned elem_size, + unsigned count, + pj_status_t (*matching)(const void *value), + void **result) +{ + unsigned i; + const char *char_array = (const char*)array; + for (i=0; i + +.global __longjmp +.type __longjmp,%function +.align 4 +__longjmp: + movl 4(%esp), %ecx /* User's jmp_buf in %ecx. */ + movl 8(%esp), %eax /* Second argument is return value. */ + /* Save the return address now. */ + movl (JB_PC*4)(%ecx), %edx + /* Restore registers. */ + movl (JB_BX*4)(%ecx), %ebx + movl (JB_SI*4)(%ecx), %esi + movl (JB_DI*4)(%ecx), %edi + movl (JB_BP*4)(%ecx), %ebp + movl (JB_SP*4)(%ecx), %esp + /* Jump to saved PC. */ + jmp *%edx +.size __longjmp,.-__longjmp + diff --git a/pjlib/src/pj/compat/setjmp_i386.S b/pjlib/src/pj/compat/setjmp_i386.S new file mode 100644 index 0000000..9cdaaff --- /dev/null +++ b/pjlib/src/pj/compat/setjmp_i386.S @@ -0,0 +1,61 @@ +/* setjmp for i386, ELF version. + Copyright (C) 1995, 1996, 1997, 2000, 2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#define _ASM +#define _SETJMP_H +#define PJ_LINUX_KERNEL 1 +#include + + +.global __sigsetjmp +.type __sigsetjmp,%function +.align 4 + +__sigsetjmp: + movl 4 (%esp), %eax + /* Save registers. */ + movl %ebx, (0 *4)(%eax) + movl %esi, (1 *4)(%eax) + movl %edi, (2 *4)(%eax) + /* Save SP as it will be after we return. */ + leal 4(%esp), %ecx + movl %ecx, (4 *4)(%eax) + /* Save PC we are returning to now. */ + movl 0(%esp), %ecx + movl %ecx, (5 *4)(%eax) + /* Save caller's frame pointer. */ + movl %ebp, (3 *4)(%eax) + + /* Make a tail call to __sigjmp_save; it takes the same args. */ +#ifdef __PIC__ + /* We cannot use the PLT, because it requires that %ebx be set, but + we can't save and restore our caller's value. Instead, we do an + indirect jump through the GOT, using for the temporary register + %ecx, which is call-clobbered. */ + call .Lhere +.Lhere: + popl %ecx + addl $_GLOBAL_OFFSET_TABLE_+[.- .Lhere ], %ecx + movl __sigjmp_save @GOT (%ecx), %ecx + jmp *%ecx +#else + jmp __sigjmp_save +#endif +.size __sigsetjmp,.-__sigsetjmp + diff --git a/pjlib/src/pj/compat/sigjmp.c b/pjlib/src/pj/compat/sigjmp.c new file mode 100644 index 0000000..912763b --- /dev/null +++ b/pjlib/src/pj/compat/sigjmp.c @@ -0,0 +1,40 @@ +/* $Id: sigjmp.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include + +int __sigjmp_save(sigjmp_buf env, int savemask) +{ + return 0; +} + +extern int __sigsetjmp(pj_jmp_buf env, int savemask); +extern void __longjmp(pj_jmp_buf env, int val) __attribute__((noreturn)); + +PJ_DEF(int) pj_setjmp(pj_jmp_buf env) +{ + return __sigsetjmp(env, 0); +} + +PJ_DEF(void) pj_longjmp(pj_jmp_buf env, int val) +{ + __longjmp(env, val); +} + diff --git a/pjlib/src/pj/compat/string.c b/pjlib/src/pj/compat/string.c new file mode 100644 index 0000000..012f79b --- /dev/null +++ b/pjlib/src/pj/compat/string.c @@ -0,0 +1,45 @@ +/* $Id: string.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include + +PJ_DEF(int) strcasecmp(const char *s1, const char *s2) +{ + while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) { + if (!*s1++) + return 0; + ++s2; + } + return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1; +} + +PJ_DEF(int) strncasecmp(const char *s1, const char *s2, int len) +{ + if (!len) return 0; + + while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) { + if (!*s1++ || --len <= 0) + return 0; + ++s2; + } + return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1; +} + diff --git a/pjlib/src/pj/compat/string_compat.c b/pjlib/src/pj/compat/string_compat.c new file mode 100644 index 0000000..e2370ac --- /dev/null +++ b/pjlib/src/pj/compat/string_compat.c @@ -0,0 +1,83 @@ +/* $Id: string_compat.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include + + +#if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H != 0 +/* Nothing to do */ +#else +PJ_DEF(int) strcasecmp(const char *s1, const char *s2) +{ + while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) { + if (!*s1++) + return 0; + ++s2; + } + return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1; +} + +PJ_DEF(int) strncasecmp(const char *s1, const char *s2, int len) +{ + if (!len) return 0; + + while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) { + if (!*s1++ || --len <= 0) + return 0; + ++s2; + } + return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1; +} +#endif + +#if defined(PJ_HAS_NO_SNPRINTF) && PJ_HAS_NO_SNPRINTF != 0 + +PJ_DEF(int) snprintf(char *s1, pj_size_t len, const char *s2, ...) +{ + int ret; + va_list arg; + + PJ_UNUSED_ARG(len); + + va_start(arg, s2); + ret = vsprintf(s1, s2, arg); + va_end(arg); + + return ret; +} + +PJ_DEF(int) vsnprintf(char *s1, pj_size_t len, const char *s2, va_list arg) +{ +#define MARK_CHAR ((char)255) + int rc; + + s1[len-1] = MARK_CHAR; + + rc = vsprintf(s1,s2,arg); + + pj_assert(s1[len-1] == MARK_CHAR || s1[len-1] == '\0'); + + return rc; +} + +#endif + diff --git a/pjlib/src/pj/config.c b/pjlib/src/pj/config.c new file mode 100644 index 0000000..709af53 --- /dev/null +++ b/pjlib/src/pj/config.c @@ -0,0 +1,89 @@ +/* $Id: config.c 4112 2012-04-27 09:47:20Z bennylp $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include + +static const char *id = "config.c"; + +#define PJ_MAKE_VERSION3_1(a,b,d) #a "." #b d +#define PJ_MAKE_VERSION3_2(a,b,d) PJ_MAKE_VERSION3_1(a,b,d) + +#define PJ_MAKE_VERSION4_1(a,b,c,d) #a "." #b "." #c d +#define PJ_MAKE_VERSION4_2(a,b,c,d) PJ_MAKE_VERSION4_1(a,b,c,d) + +#if PJ_VERSION_NUM_REV +PJ_DEF_DATA(const char*) PJ_VERSION = PJ_MAKE_VERSION4_2(PJ_VERSION_NUM_MAJOR, + PJ_VERSION_NUM_MINOR, + PJ_VERSION_NUM_REV, + PJ_VERSION_NUM_EXTRA); +#else +PJ_DEF_DATA(const char*) PJ_VERSION = PJ_MAKE_VERSION3_2(PJ_VERSION_NUM_MAJOR, + PJ_VERSION_NUM_MINOR, + PJ_VERSION_NUM_EXTRA); +#endif + +/* + * Get PJLIB version string. + */ +PJ_DEF(const char*) pj_get_version(void) +{ + return PJ_VERSION; +} + +PJ_DEF(void) pj_dump_config(void) +{ + PJ_LOG(3, (id, "PJLIB (c)2008-2009 Teluu Inc.")); + PJ_LOG(3, (id, "Dumping configurations:")); + PJ_LOG(3, (id, " PJ_VERSION : %s", PJ_VERSION)); + PJ_LOG(3, (id, " PJ_M_NAME : %s", PJ_M_NAME)); + PJ_LOG(3, (id, " PJ_HAS_PENTIUM : %d", PJ_HAS_PENTIUM)); + PJ_LOG(3, (id, " PJ_OS_NAME : %s", PJ_OS_NAME)); + PJ_LOG(3, (id, " PJ_CC_NAME/VER_(1,2,3) : %s-%d.%d.%d", PJ_CC_NAME, + PJ_CC_VER_1, PJ_CC_VER_2, PJ_CC_VER_3)); + PJ_LOG(3, (id, " PJ_IS_(BIG/LITTLE)_ENDIAN : %s", + (PJ_IS_BIG_ENDIAN?"big-endian":"little-endian"))); + PJ_LOG(3, (id, " PJ_HAS_INT64 : %d", PJ_HAS_INT64)); + PJ_LOG(3, (id, " PJ_HAS_FLOATING_POINT : %d", PJ_HAS_FLOATING_POINT)); + PJ_LOG(3, (id, " PJ_DEBUG : %d", PJ_DEBUG)); + PJ_LOG(3, (id, " PJ_FUNCTIONS_ARE_INLINED : %d", PJ_FUNCTIONS_ARE_INLINED)); + PJ_LOG(3, (id, " PJ_LOG_MAX_LEVEL : %d", PJ_LOG_MAX_LEVEL)); + PJ_LOG(3, (id, " PJ_LOG_MAX_SIZE : %d", PJ_LOG_MAX_SIZE)); + PJ_LOG(3, (id, " PJ_LOG_USE_STACK_BUFFER : %d", PJ_LOG_USE_STACK_BUFFER)); + PJ_LOG(3, (id, " PJ_POOL_DEBUG : %d", PJ_POOL_DEBUG)); + PJ_LOG(3, (id, " PJ_HAS_POOL_ALT_API : %d", PJ_HAS_POOL_ALT_API)); + PJ_LOG(3, (id, " PJ_HAS_TCP : %d", PJ_HAS_TCP)); + PJ_LOG(3, (id, " PJ_MAX_HOSTNAME : %d", PJ_MAX_HOSTNAME)); + PJ_LOG(3, (id, " ioqueue type : %s", pj_ioqueue_name())); + PJ_LOG(3, (id, " PJ_IOQUEUE_MAX_HANDLES : %d", PJ_IOQUEUE_MAX_HANDLES)); + PJ_LOG(3, (id, " PJ_IOQUEUE_HAS_SAFE_UNREG : %d", PJ_IOQUEUE_HAS_SAFE_UNREG)); + PJ_LOG(3, (id, " PJ_HAS_THREADS : %d", PJ_HAS_THREADS)); + PJ_LOG(3, (id, " PJ_LOG_USE_STACK_BUFFER : %d", PJ_LOG_USE_STACK_BUFFER)); + PJ_LOG(3, (id, " PJ_HAS_SEMAPHORE : %d", PJ_HAS_SEMAPHORE)); + PJ_LOG(3, (id, " PJ_HAS_EVENT_OBJ : %d", PJ_HAS_EVENT_OBJ)); + PJ_LOG(3, (id, " PJ_ENABLE_EXTRA_CHECK : %d", PJ_ENABLE_EXTRA_CHECK)); + PJ_LOG(3, (id, " PJ_HAS_EXCEPTION_NAMES : %d", PJ_HAS_EXCEPTION_NAMES)); + PJ_LOG(3, (id, " PJ_MAX_EXCEPTION_ID : %d", PJ_MAX_EXCEPTION_ID)); + PJ_LOG(3, (id, " PJ_EXCEPTION_USE_WIN32_SEH: %d", PJ_EXCEPTION_USE_WIN32_SEH)); + PJ_LOG(3, (id, " PJ_TIMESTAMP_USE_RDTSC: : %d", PJ_TIMESTAMP_USE_RDTSC)); + PJ_LOG(3, (id, " PJ_OS_HAS_CHECK_STACK : %d", PJ_OS_HAS_CHECK_STACK)); + PJ_LOG(3, (id, " PJ_HAS_HIGH_RES_TIMER : %d", PJ_HAS_HIGH_RES_TIMER)); +} + diff --git a/pjlib/src/pj/ctype.c b/pjlib/src/pj/ctype.c new file mode 100644 index 0000000..bb04342 --- /dev/null +++ b/pjlib/src/pj/ctype.c @@ -0,0 +1,28 @@ +/* $Id: ctype.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + +/* +char pj_hex_digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; +*/ + +int pjlib_ctype_c_dummy_symbol; + diff --git a/pjlib/src/pj/errno.c b/pjlib/src/pj/errno.c new file mode 100644 index 0000000..1cb8e72 --- /dev/null +++ b/pjlib/src/pj/errno.c @@ -0,0 +1,320 @@ +/* $Id: errno.c 3664 2011-07-19 03:42:28Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include + +/* Prototype for platform specific error message, which will be defined + * in separate file. + */ +PJ_BEGIN_DECL + + PJ_DECL(int) platform_strerror(pj_os_err_type code, + char *buf, pj_size_t bufsize ); +PJ_END_DECL + +#ifndef PJLIB_MAX_ERR_MSG_HANDLER +# define PJLIB_MAX_ERR_MSG_HANDLER 10 +#endif + +/* Error message handler. */ +static unsigned err_msg_hnd_cnt; +static struct err_msg_hnd +{ + pj_status_t begin; + pj_status_t end; + pj_str_t (*strerror)(pj_status_t, char*, pj_size_t); + +} err_msg_hnd[PJLIB_MAX_ERR_MSG_HANDLER]; + +/* PJLIB's own error codes/messages */ +#if defined(PJ_HAS_ERROR_STRING) && PJ_HAS_ERROR_STRING!=0 + +static const struct +{ + int code; + const char *msg; +} err_str[] = +{ + PJ_BUILD_ERR(PJ_EUNKNOWN, "Unknown Error" ), + PJ_BUILD_ERR(PJ_EPENDING, "Pending operation" ), + PJ_BUILD_ERR(PJ_ETOOMANYCONN, "Too many connecting sockets" ), + PJ_BUILD_ERR(PJ_EINVAL, "Invalid value or argument" ), + PJ_BUILD_ERR(PJ_ENAMETOOLONG, "Name too long" ), + PJ_BUILD_ERR(PJ_ENOTFOUND, "Not found" ), + PJ_BUILD_ERR(PJ_ENOMEM, "Not enough memory" ), + PJ_BUILD_ERR(PJ_EBUG, "BUG DETECTED!" ), + PJ_BUILD_ERR(PJ_ETIMEDOUT, "Operation timed out" ), + PJ_BUILD_ERR(PJ_ETOOMANY, "Too many objects of the specified type"), + PJ_BUILD_ERR(PJ_EBUSY, "Object is busy"), + PJ_BUILD_ERR(PJ_ENOTSUP, "Option/operation is not supported"), + PJ_BUILD_ERR(PJ_EINVALIDOP, "Invalid operation"), + PJ_BUILD_ERR(PJ_ECANCELLED, "Operation cancelled"), + PJ_BUILD_ERR(PJ_EEXISTS, "Object already exists" ), + PJ_BUILD_ERR(PJ_EEOF, "End of file" ), + PJ_BUILD_ERR(PJ_ETOOBIG, "Size is too big"), + PJ_BUILD_ERR(PJ_ERESOLVE, "gethostbyname() has returned error"), + PJ_BUILD_ERR(PJ_ETOOSMALL, "Size is too short"), + PJ_BUILD_ERR(PJ_EIGNORED, "Ignored"), + PJ_BUILD_ERR(PJ_EIPV6NOTSUP, "IPv6 is not supported"), + PJ_BUILD_ERR(PJ_EAFNOTSUP, "Unsupported address family") +}; +#endif /* PJ_HAS_ERROR_STRING */ + + +/* + * pjlib_error() + * + * Retrieve message string for PJLIB's own error code. + */ +static int pjlib_error(pj_status_t code, char *buf, pj_size_t size) +{ +#if defined(PJ_HAS_ERROR_STRING) && PJ_HAS_ERROR_STRING!=0 + unsigned i; + + for (i=0; i= size) len = size-1; + pj_memcpy(buf, err_str[i].msg, len); + buf[len] = '\0'; + return len; + } + } +#endif + + return pj_ansi_snprintf( buf, size, "Unknown pjlib error %d", code); +} + +#define IN_RANGE(val,start,end) ((val)>=(start) && (val)<(end)) + +/* Register strerror handle. */ +PJ_DEF(pj_status_t) pj_register_strerror( pj_status_t start, + pj_status_t space, + pj_error_callback f) +{ + unsigned i; + + /* Check arguments. */ + PJ_ASSERT_RETURN(start && space && f, PJ_EINVAL); + + /* Check if there aren't too many handlers registered. */ + PJ_ASSERT_RETURN(err_msg_hnd_cnt < PJ_ARRAY_SIZE(err_msg_hnd), + PJ_ETOOMANY); + + /* Start error must be greater than PJ_ERRNO_START_USER */ + PJ_ASSERT_RETURN(start >= PJ_ERRNO_START_USER, PJ_EEXISTS); + + /* Check that no existing handler has covered the specified range. */ + for (i=0; i= 1 +static void invoke_log(const char *sender, int level, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(sender, level, format, arg); + va_end(arg); +} + +static void pj_perror_imp(int log_level, const char *sender, + pj_status_t status, + const char *title_fmt, va_list marker) +{ + char titlebuf[PJ_PERROR_TITLE_BUF_SIZE]; + char errmsg[PJ_ERR_MSG_SIZE]; + int len; + + /* Build the title */ + len = pj_ansi_vsnprintf(titlebuf, sizeof(titlebuf), title_fmt, marker); + if (len < 0 || len >= (int)sizeof(titlebuf)) + pj_ansi_strcpy(titlebuf, "Error"); + + /* Get the error */ + pj_strerror(status, errmsg, sizeof(errmsg)); + + /* Send to log */ + invoke_log(sender, log_level, "%s: %s", titlebuf, errmsg); +} + +PJ_DEF(void) pj_perror(int log_level, const char *sender, pj_status_t status, + const char *title_fmt, ...) +{ + va_list marker; + va_start(marker, title_fmt); + pj_perror_imp(log_level, sender, status, title_fmt, marker); + va_end(marker); +} + +PJ_DEF(void) pj_perror_1(const char *sender, pj_status_t status, + const char *title_fmt, ...) +{ + va_list marker; + va_start(marker, title_fmt); + pj_perror_imp(1, sender, status, title_fmt, marker); + va_end(marker); +} + +#else /* #if PJ_LOG_MAX_LEVEL >= 1 */ +PJ_DEF(void) pj_perror(int log_level, const char *sender, pj_status_t status, + const char *title_fmt, ...) +{ +} +#endif /* #if PJ_LOG_MAX_LEVEL >= 1 */ + + +#if PJ_LOG_MAX_LEVEL >= 2 +PJ_DEF(void) pj_perror_2(const char *sender, pj_status_t status, + const char *title_fmt, ...) +{ + va_list marker; + va_start(marker, title_fmt); + pj_perror_imp(2, sender, status, title_fmt, marker); + va_end(marker); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 3 +PJ_DEF(void) pj_perror_3(const char *sender, pj_status_t status, + const char *title_fmt, ...) +{ + va_list marker; + va_start(marker, title_fmt); + pj_perror_imp(3, sender, status, title_fmt, marker); + va_end(marker); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 4 +PJ_DEF(void) pj_perror_4(const char *sender, pj_status_t status, + const char *title_fmt, ...) +{ + va_list marker; + va_start(marker, title_fmt); + pj_perror_imp(4, sender, status, title_fmt, marker); + va_end(marker); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 5 +PJ_DEF(void) pj_perror_5(const char *sender, pj_status_t status, + const char *title_fmt, ...) +{ + va_list marker; + va_start(marker, title_fmt); + pj_perror_imp(5, sender, status, title_fmt, marker); + va_end(marker); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 6 +PJ_DEF(void) pj_perror_6(const char *sender, pj_status_t status, + const char *title_fmt, ...) +{ + va_list marker; + va_start(marker, title_fmt); + pj_perror_imp(6, sender, status, title_fmt, marker); + va_end(marker); +} +#endif + diff --git a/pjlib/src/pj/except.c b/pjlib/src/pj/except.c new file mode 100644 index 0000000..e3984df --- /dev/null +++ b/pjlib/src/pj/except.c @@ -0,0 +1,185 @@ +/* $Id: except.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include + +static long thread_local_id = -1; + +#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 + static const char *exception_id_names[PJ_MAX_EXCEPTION_ID]; +#else + /* + * Start from 1 (not 0)!!! + * Exception 0 is reserved for normal path of setjmp()!!! + */ + static int last_exception_id = 1; +#endif /* PJ_HAS_EXCEPTION_NAMES */ + + +#if !defined(PJ_EXCEPTION_USE_WIN32_SEH) || PJ_EXCEPTION_USE_WIN32_SEH==0 +PJ_DEF(void) pj_throw_exception_(int exception_id) +{ + struct pj_exception_state_t *handler; + + handler = (struct pj_exception_state_t*) + pj_thread_local_get(thread_local_id); + if (handler == NULL) { + PJ_LOG(1,("except.c", "!!!FATAL: unhandled exception %s!\n", + pj_exception_id_name(exception_id))); + pj_assert(handler != NULL); + /* This will crash the system! */ + } + pj_pop_exception_handler_(handler); + pj_longjmp(handler->state, exception_id); +} + +static void exception_cleanup(void) +{ + if (thread_local_id != -1) { + pj_thread_local_free(thread_local_id); + thread_local_id = -1; + } + +#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 + { + unsigned i; + for (i=0; iprev = parent_handler; + pj_thread_local_set(thread_local_id, rec); +} + +PJ_DEF(void) pj_pop_exception_handler_(struct pj_exception_state_t *rec) +{ + struct pj_exception_state_t *handler; + + handler = (struct pj_exception_state_t *) + pj_thread_local_get(thread_local_id); + if (handler && handler==rec) { + pj_thread_local_set(thread_local_id, handler->prev); + } +} +#endif + +#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 +PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name, + pj_exception_id_t *id) +{ + unsigned i; + + pj_enter_critical_section(); + + /* + * Start from 1 (not 0)!!! + * Exception 0 is reserved for normal path of setjmp()!!! + */ + for (i=1; i0 && id0 && id"); + + if (exception_id_names[id] == NULL) { + pj_ansi_snprintf(unknown_name, sizeof(unknown_name), + "exception %d", id); + return unknown_name; + } + + return exception_id_names[id]; +} + +#else /* PJ_HAS_EXCEPTION_NAMES */ +PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name, + pj_exception_id_t *id) +{ + PJ_ASSERT_RETURN(last_exception_id < PJ_MAX_EXCEPTION_ID-1, PJ_ETOOMANY); + + *id = last_exception_id++; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id ) +{ + return PJ_SUCCESS; +} + +PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id) +{ + return ""; +} + +#endif /* PJ_HAS_EXCEPTION_NAMES */ + + + diff --git a/pjlib/src/pj/exception_symbian.cpp b/pjlib/src/pj/exception_symbian.cpp new file mode 100644 index 0000000..f2dae4c --- /dev/null +++ b/pjlib/src/pj/exception_symbian.cpp @@ -0,0 +1,116 @@ +/* $Id: exception_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include + + +#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 + static const char *exception_id_names[PJ_MAX_EXCEPTION_ID]; +#else + /* + * Start from 1 (not 0)!!! + * Exception 0 is reserved for normal path of setjmp()!!! + */ + static int last_exception_id = 1; +#endif /* PJ_HAS_EXCEPTION_NAMES */ + + +#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 +PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name, + pj_exception_id_t *id) +{ + unsigned i; + + pj_enter_critical_section(); + + /* + * Start from 1 (not 0)!!! + * Exception 0 is reserved for normal path of setjmp()!!! + */ + for (i=1; i0 && id0 && id"); + + if (exception_id_names[id] == NULL) + return ""; + + return exception_id_names[id]; +} + +#else /* PJ_HAS_EXCEPTION_NAMES */ +PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name, + pj_exception_id_t *id) +{ + PJ_ASSERT_RETURN(last_exception_id < PJ_MAX_EXCEPTION_ID-1, PJ_ETOOMANY); + + *id = last_exception_id++; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id ) +{ + return PJ_SUCCESS; +} + +PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id) +{ + return ""; +} + +#endif /* PJ_HAS_EXCEPTION_NAMES */ + + + diff --git a/pjlib/src/pj/extra-exports.c b/pjlib/src/pj/extra-exports.c new file mode 100644 index 0000000..dcd5e5e --- /dev/null +++ b/pjlib/src/pj/extra-exports.c @@ -0,0 +1,41 @@ +/* $Id: extra-exports.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include + +EXPORT_SYMBOL(sys_select); + +EXPORT_SYMBOL(sys_epoll_create); +EXPORT_SYMBOL(sys_epoll_ctl); +EXPORT_SYMBOL(sys_epoll_wait); + +EXPORT_SYMBOL(sys_socket); +EXPORT_SYMBOL(sys_bind); +EXPORT_SYMBOL(sys_getpeername); +EXPORT_SYMBOL(sys_getsockname); +EXPORT_SYMBOL(sys_sendto); +EXPORT_SYMBOL(sys_recvfrom); +EXPORT_SYMBOL(sys_getsockopt); +EXPORT_SYMBOL(sys_setsockopt); +EXPORT_SYMBOL(sys_listen); +EXPORT_SYMBOL(sys_shutdown); +EXPORT_SYMBOL(sys_connect); +EXPORT_SYMBOL(sys_accept); + diff --git a/pjlib/src/pj/fifobuf.c b/pjlib/src/pj/fifobuf.c new file mode 100644 index 0000000..58620ea --- /dev/null +++ b/pjlib/src/pj/fifobuf.c @@ -0,0 +1,189 @@ +/* $Id: fifobuf.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include + +#define THIS_FILE "fifobuf" + +#define SZ sizeof(unsigned) + +PJ_DEF(void) pj_fifobuf_init (pj_fifobuf_t *fifobuf, void *buffer, unsigned size) +{ + PJ_CHECK_STACK(); + + PJ_LOG(6, (THIS_FILE, + "fifobuf_init fifobuf=%p buffer=%p, size=%d", + fifobuf, buffer, size)); + + fifobuf->first = (char*)buffer; + fifobuf->last = fifobuf->first + size; + fifobuf->ubegin = fifobuf->uend = fifobuf->first; + fifobuf->full = 0; +} + +PJ_DEF(unsigned) pj_fifobuf_max_size (pj_fifobuf_t *fifobuf) +{ + unsigned s1, s2; + + PJ_CHECK_STACK(); + + if (fifobuf->uend >= fifobuf->ubegin) { + s1 = fifobuf->last - fifobuf->uend; + s2 = fifobuf->ubegin - fifobuf->first; + } else { + s1 = s2 = fifobuf->ubegin - fifobuf->uend; + } + + return s1full) { + PJ_LOG(6, (THIS_FILE, + "fifobuf_alloc fifobuf=%p, size=%d: full!", + fifobuf, size)); + return NULL; + } + + /* try to allocate from the end part of the fifo */ + if (fifobuf->uend >= fifobuf->ubegin) { + available = fifobuf->last - fifobuf->uend; + if (available >= size+SZ) { + char *ptr = fifobuf->uend; + fifobuf->uend += (size+SZ); + if (fifobuf->uend == fifobuf->last) + fifobuf->uend = fifobuf->first; + if (fifobuf->uend == fifobuf->ubegin) + fifobuf->full = 1; + *(unsigned*)ptr = size+SZ; + ptr += SZ; + + PJ_LOG(6, (THIS_FILE, + "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", + fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend)); + return ptr; + } + } + + /* try to allocate from the start part of the fifo */ + start = (fifobuf->uend <= fifobuf->ubegin) ? fifobuf->uend : fifobuf->first; + available = fifobuf->ubegin - start; + if (available >= size+SZ) { + char *ptr = start; + fifobuf->uend = start + size + SZ; + if (fifobuf->uend == fifobuf->ubegin) + fifobuf->full = 1; + *(unsigned*)ptr = size+SZ; + ptr += SZ; + + PJ_LOG(6, (THIS_FILE, + "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", + fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend)); + return ptr; + } + + PJ_LOG(6, (THIS_FILE, + "fifobuf_alloc fifobuf=%p, size=%d: no space left! p1=%p, p2=%p", + fifobuf, size, fifobuf->ubegin, fifobuf->uend)); + return NULL; +} + +PJ_DEF(pj_status_t) pj_fifobuf_unalloc (pj_fifobuf_t *fifobuf, void *buf) +{ + char *ptr = (char*)buf; + char *endptr; + unsigned sz; + + PJ_CHECK_STACK(); + + ptr -= SZ; + sz = *(unsigned*)ptr; + + endptr = fifobuf->uend; + if (endptr == fifobuf->first) + endptr = fifobuf->last; + + if (ptr+sz != endptr) { + pj_assert(!"Invalid pointer to undo alloc"); + return -1; + } + + fifobuf->uend = ptr; + fifobuf->full = 0; + + PJ_LOG(6, (THIS_FILE, + "fifobuf_unalloc fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p", + fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend)); + + return 0; +} + +PJ_DEF(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fifobuf, void *buf) +{ + char *ptr = (char*)buf; + char *end; + unsigned sz; + + PJ_CHECK_STACK(); + + ptr -= SZ; + if (ptr < fifobuf->first || ptr >= fifobuf->last) { + pj_assert(!"Invalid pointer to free"); + return -1; + } + + if (ptr != fifobuf->ubegin && ptr != fifobuf->first) { + pj_assert(!"Invalid free() sequence!"); + return -1; + } + + end = (fifobuf->uend > fifobuf->ubegin) ? fifobuf->uend : fifobuf->last; + sz = *(unsigned*)ptr; + if (ptr+sz > end) { + pj_assert(!"Invalid size!"); + return -1; + } + + fifobuf->ubegin = ptr + sz; + + /* Rollover */ + if (fifobuf->ubegin == fifobuf->last) + fifobuf->ubegin = fifobuf->first; + + /* Reset if fifobuf is empty */ + if (fifobuf->ubegin == fifobuf->uend) + fifobuf->ubegin = fifobuf->uend = fifobuf->first; + + fifobuf->full = 0; + + PJ_LOG(6, (THIS_FILE, + "fifobuf_free fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p", + fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend)); + + return 0; +} diff --git a/pjlib/src/pj/file_access_unistd.c b/pjlib/src/pj/file_access_unistd.c new file mode 100644 index 0000000..b48a3bc --- /dev/null +++ b/pjlib/src/pj/file_access_unistd.c @@ -0,0 +1,114 @@ +/* $Id: file_access_unistd.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include + +#include +#include +#include +#include /* rename() */ +#include + +/* + * pj_file_exists() + */ +PJ_DEF(pj_bool_t) pj_file_exists(const char *filename) +{ + struct stat buf; + + PJ_ASSERT_RETURN(filename, 0); + + if (stat(filename, &buf) != 0) + return 0; + + return PJ_TRUE; +} + + +/* + * pj_file_size() + */ +PJ_DEF(pj_off_t) pj_file_size(const char *filename) +{ + struct stat buf; + + PJ_ASSERT_RETURN(filename, -1); + + if (stat(filename, &buf) != 0) + return -1; + + return buf.st_size; +} + + +/* + * pj_file_delete() + */ +PJ_DEF(pj_status_t) pj_file_delete(const char *filename) +{ + PJ_ASSERT_RETURN(filename, PJ_EINVAL); + + if (unlink(filename)!=0) { + return PJ_RETURN_OS_ERROR(errno); + } + return PJ_SUCCESS; +} + + +/* + * pj_file_move() + */ +PJ_DEF(pj_status_t) pj_file_move( const char *oldname, const char *newname) +{ + PJ_ASSERT_RETURN(oldname && newname, PJ_EINVAL); + + if (rename(oldname, newname) != 0) { + return PJ_RETURN_OS_ERROR(errno); + } + return PJ_SUCCESS; +} + + +/* + * pj_file_getstat() + */ +PJ_DEF(pj_status_t) pj_file_getstat(const char *filename, + pj_file_stat *statbuf) +{ + struct stat buf; + + PJ_ASSERT_RETURN(filename && statbuf, PJ_EINVAL); + + if (stat(filename, &buf) != 0) { + return PJ_RETURN_OS_ERROR(errno); + } + + statbuf->size = buf.st_size; + statbuf->ctime.sec = buf.st_ctime; + statbuf->ctime.msec = 0; + statbuf->mtime.sec = buf.st_mtime; + statbuf->mtime.msec = 0; + statbuf->atime.sec = buf.st_atime; + statbuf->atime.msec = 0; + + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/file_access_win32.c b/pjlib/src/pj/file_access_win32.c new file mode 100644 index 0000000..e737fff --- /dev/null +++ b/pjlib/src/pj/file_access_win32.c @@ -0,0 +1,215 @@ +/* $Id: file_access_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 + /* WinCE lacks READ_CONTROL so we must use GENERIC_READ */ +# define CONTROL_ACCESS GENERIC_READ +#else +# define CONTROL_ACCESS READ_CONTROL +#endif + + +/* + * pj_file_exists() + */ +PJ_DEF(pj_bool_t) pj_file_exists(const char *filename) +{ + PJ_DECL_UNICODE_TEMP_BUF(wfilename,256) + HANDLE hFile; + + PJ_ASSERT_RETURN(filename != NULL, 0); + + hFile = CreateFile(PJ_STRING_TO_NATIVE(filename,wfilename,sizeof(wfilename)), + CONTROL_ACCESS, + FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return 0; + + CloseHandle(hFile); + return PJ_TRUE; +} + + +/* + * pj_file_size() + */ +PJ_DEF(pj_off_t) pj_file_size(const char *filename) +{ + PJ_DECL_UNICODE_TEMP_BUF(wfilename,256) + HANDLE hFile; + DWORD sizeLo, sizeHi; + pj_off_t size; + + PJ_ASSERT_RETURN(filename != NULL, -1); + + hFile = CreateFile(PJ_STRING_TO_NATIVE(filename, wfilename,sizeof(wfilename)), + CONTROL_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return -1; + + sizeLo = GetFileSize(hFile, &sizeHi); + if (sizeLo == INVALID_FILE_SIZE) { + DWORD dwStatus = GetLastError(); + if (dwStatus != NO_ERROR) { + CloseHandle(hFile); + return -1; + } + } + + size = sizeHi; + size = (size << 32) + sizeLo; + + CloseHandle(hFile); + return size; +} + + +/* + * pj_file_delete() + */ +PJ_DEF(pj_status_t) pj_file_delete(const char *filename) +{ + PJ_DECL_UNICODE_TEMP_BUF(wfilename,256) + + PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL); + + if (DeleteFile(PJ_STRING_TO_NATIVE(filename,wfilename,sizeof(wfilename))) == FALSE) + return PJ_RETURN_OS_ERROR(GetLastError()); + + return PJ_SUCCESS; +} + + +/* + * pj_file_move() + */ +PJ_DEF(pj_status_t) pj_file_move( const char *oldname, const char *newname) +{ + PJ_DECL_UNICODE_TEMP_BUF(woldname,256) + PJ_DECL_UNICODE_TEMP_BUF(wnewname,256) + BOOL rc; + + PJ_ASSERT_RETURN(oldname!=NULL && newname!=NULL, PJ_EINVAL); + +#if PJ_WIN32_WINNT >= 0x0400 + rc = MoveFileEx(PJ_STRING_TO_NATIVE(oldname,woldname,sizeof(woldname)), + PJ_STRING_TO_NATIVE(newname,wnewname,sizeof(wnewname)), + MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING); +#else + rc = MoveFile(PJ_STRING_TO_NATIVE(oldname,woldname,sizeof(woldname)), + PJ_STRING_TO_NATIVE(newname,wnewname,sizeof(wnewname))); +#endif + + if (!rc) + return PJ_RETURN_OS_ERROR(GetLastError()); + + return PJ_SUCCESS; +} + + +static pj_status_t file_time_to_time_val(const FILETIME *file_time, + pj_time_val *time_val) +{ + FILETIME local_file_time; + SYSTEMTIME localTime; + pj_parsed_time pt; + + if (!FileTimeToLocalFileTime(file_time, &local_file_time)) + return PJ_RETURN_OS_ERROR(GetLastError()); + + if (!FileTimeToSystemTime(file_time, &localTime)) + return PJ_RETURN_OS_ERROR(GetLastError()); + + //if (!SystemTimeToTzSpecificLocalTime(NULL, &systemTime, &localTime)) + // return PJ_RETURN_OS_ERROR(GetLastError()); + + pj_bzero(&pt, sizeof(pt)); + pt.year = localTime.wYear; + pt.mon = localTime.wMonth-1; + pt.day = localTime.wDay; + pt.wday = localTime.wDayOfWeek; + + pt.hour = localTime.wHour; + pt.min = localTime.wMinute; + pt.sec = localTime.wSecond; + pt.msec = localTime.wMilliseconds; + + return pj_time_encode(&pt, time_val); +} + +/* + * pj_file_getstat() + */ +PJ_DEF(pj_status_t) pj_file_getstat(const char *filename, pj_file_stat *stat) +{ + PJ_DECL_UNICODE_TEMP_BUF(wfilename,256) + HANDLE hFile; + DWORD sizeLo, sizeHi; + FILETIME creationTime, accessTime, writeTime; + + PJ_ASSERT_RETURN(filename!=NULL && stat!=NULL, PJ_EINVAL); + + hFile = CreateFile(PJ_STRING_TO_NATIVE(filename,wfilename,sizeof(wfilename)), + CONTROL_ACCESS, + FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return PJ_RETURN_OS_ERROR(GetLastError()); + + sizeLo = GetFileSize(hFile, &sizeHi); + if (sizeLo == INVALID_FILE_SIZE) { + DWORD dwStatus = GetLastError(); + if (dwStatus != NO_ERROR) { + CloseHandle(hFile); + return PJ_RETURN_OS_ERROR(dwStatus); + } + } + + stat->size = sizeHi; + stat->size = (stat->size << 32) + sizeLo; + + if (GetFileTime(hFile, &creationTime, &accessTime, &writeTime)==FALSE) { + DWORD dwStatus = GetLastError(); + CloseHandle(hFile); + return PJ_RETURN_OS_ERROR(dwStatus); + } + + CloseHandle(hFile); + + if (file_time_to_time_val(&creationTime, &stat->ctime) != PJ_SUCCESS) + return PJ_RETURN_OS_ERROR(GetLastError()); + + file_time_to_time_val(&accessTime, &stat->atime); + file_time_to_time_val(&writeTime, &stat->mtime); + + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/file_io_ansi.c b/pjlib/src/pj/file_io_ansi.c new file mode 100644 index 0000000..487d496 --- /dev/null +++ b/pjlib/src/pj/file_io_ansi.c @@ -0,0 +1,170 @@ +/* $Id: file_io_ansi.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include + +PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool, + const char *pathname, + unsigned flags, + pj_oshandle_t *fd) +{ + char mode[8]; + char *p = mode; + + PJ_ASSERT_RETURN(pathname && fd, PJ_EINVAL); + PJ_UNUSED_ARG(pool); + + if ((flags & PJ_O_APPEND) == PJ_O_APPEND) { + if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) { + *p++ = 'a'; + if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) + *p++ = '+'; + } else { + /* This is invalid. + * Can not specify PJ_O_RDONLY with PJ_O_APPEND! + */ + } + } else { + if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) { + *p++ = 'r'; + if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) + *p++ = '+'; + } else { + *p++ = 'w'; + } + } + + if (p==mode) + return PJ_EINVAL; + + *p++ = 'b'; + *p++ = '\0'; + + *fd = fopen(pathname, mode); + if (*fd == NULL) + return PJ_RETURN_OS_ERROR(errno); + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd) +{ + PJ_ASSERT_RETURN(fd, PJ_EINVAL); + if (fclose((FILE*)fd) != 0) + return PJ_RETURN_OS_ERROR(errno); + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd, + const void *data, + pj_ssize_t *size) +{ + size_t written; + + clearerr((FILE*)fd); + written = fwrite(data, 1, *size, (FILE*)fd); + if (ferror((FILE*)fd)) { + *size = -1; + return PJ_RETURN_OS_ERROR(errno); + } + + *size = written; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd, + void *data, + pj_ssize_t *size) +{ + size_t bytes; + + clearerr((FILE*)fd); + bytes = fread(data, 1, *size, (FILE*)fd); + if (ferror((FILE*)fd)) { + *size = -1; + return PJ_RETURN_OS_ERROR(errno); + } + + *size = bytes; + return PJ_SUCCESS; +} + +/* +PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access) +{ + PJ_UNUSED_ARG(access); + return feof((FILE*)fd) ? PJ_TRUE : 0; +} +*/ + +PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd, + pj_off_t offset, + enum pj_file_seek_type whence) +{ + int mode; + + switch (whence) { + case PJ_SEEK_SET: + mode = SEEK_SET; break; + case PJ_SEEK_CUR: + mode = SEEK_CUR; break; + case PJ_SEEK_END: + mode = SEEK_END; break; + default: + pj_assert(!"Invalid whence in file_setpos"); + return PJ_EINVAL; + } + + if (fseek((FILE*)fd, (long)offset, mode) != 0) + return PJ_RETURN_OS_ERROR(errno); + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd, + pj_off_t *pos) +{ + long offset; + + offset = ftell((FILE*)fd); + if (offset == -1) { + *pos = -1; + return PJ_RETURN_OS_ERROR(errno); + } + + *pos = offset; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_flush(pj_oshandle_t fd) +{ + int rc; + + rc = fflush((FILE*)fd); + if (rc == EOF) { + return PJ_RETURN_OS_ERROR(errno); + } + + return PJ_SUCCESS; +} diff --git a/pjlib/src/pj/file_io_win32.c b/pjlib/src/pj/file_io_win32.c new file mode 100644 index 0000000..42ccd70 --- /dev/null +++ b/pjlib/src/pj/file_io_win32.c @@ -0,0 +1,240 @@ +/* $Id: file_io_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include + +#include + +#ifndef INVALID_SET_FILE_POINTER +# define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +/** + * Check for end-of-file condition on the specified descriptor. + * + * @param fd The file descriptor. + * @param access The desired access. + * + * @return Non-zero if file is EOF. + */ +PJ_DECL(pj_bool_t) pj_file_eof(pj_oshandle_t fd, + enum pj_file_access access); + + +PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool, + const char *pathname, + unsigned flags, + pj_oshandle_t *fd) +{ + PJ_DECL_UNICODE_TEMP_BUF(wpathname,256) + HANDLE hFile; + DWORD dwDesiredAccess = 0; + DWORD dwShareMode = 0; + DWORD dwCreationDisposition = 0; + DWORD dwFlagsAndAttributes = 0; + + PJ_UNUSED_ARG(pool); + + PJ_ASSERT_RETURN(pathname!=NULL, PJ_EINVAL); + + if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) { + dwDesiredAccess |= GENERIC_WRITE; + if ((flags & PJ_O_APPEND) == PJ_O_APPEND) { +#if !defined(PJ_WIN32_WINCE) || !PJ_WIN32_WINCE + /* FILE_APPEND_DATA is invalid on WM2003 and WM5, but it seems + * to be working on WM6. All are tested on emulator though. + * Removing this also seem to work (i.e. data is appended), so + * I guess this flag is "optional". + * See http://trac.pjsip.org/repos/ticket/825 + */ + dwDesiredAccess |= FILE_APPEND_DATA; +#endif + dwCreationDisposition |= OPEN_ALWAYS; + } else { + dwDesiredAccess &= ~(FILE_APPEND_DATA); + dwCreationDisposition |= CREATE_ALWAYS; + } + } + if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) { + dwDesiredAccess |= GENERIC_READ; + if (flags == PJ_O_RDONLY) + dwCreationDisposition |= OPEN_EXISTING; + } + + if (dwDesiredAccess == 0) { + pj_assert(!"Invalid file open flags"); + return PJ_EINVAL; + } + + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; + + hFile = CreateFile(PJ_STRING_TO_NATIVE(pathname,wpathname,sizeof(wpathname)), + dwDesiredAccess, dwShareMode, NULL, + dwCreationDisposition, dwFlagsAndAttributes, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + *fd = 0; + return PJ_RETURN_OS_ERROR(GetLastError()); + } + + if ((flags & PJ_O_APPEND) == PJ_O_APPEND) { + pj_status_t status; + + status = pj_file_setpos(hFile, 0, PJ_SEEK_END); + if (status != PJ_SUCCESS) { + pj_file_close(hFile); + return status; + } + } + + *fd = hFile; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd) +{ + if (CloseHandle(fd)==0) + return PJ_RETURN_OS_ERROR(GetLastError()); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd, + const void *data, + pj_ssize_t *size) +{ + BOOL rc; + DWORD bytesWritten; + + rc = WriteFile(fd, data, *size, &bytesWritten, NULL); + if (!rc) { + *size = -1; + return PJ_RETURN_OS_ERROR(GetLastError()); + } + + *size = bytesWritten; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd, + void *data, + pj_ssize_t *size) +{ + BOOL rc; + DWORD bytesRead; + + rc = ReadFile(fd, data, *size, &bytesRead, NULL); + if (!rc) { + *size = -1; + return PJ_RETURN_OS_ERROR(GetLastError()); + } + + *size = bytesRead; + return PJ_SUCCESS; +} + +/* +PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access) +{ + BOOL rc; + DWORD dummy = 0, bytes; + DWORD dwStatus; + + if ((access & PJ_O_RDONLY) == PJ_O_RDONLY) { + rc = ReadFile(fd, &dummy, 0, &bytes, NULL); + } else if ((access & PJ_O_WRONLY) == PJ_O_WRONLY) { + rc = WriteFile(fd, &dummy, 0, &bytes, NULL); + } else { + pj_assert(!"Invalid access"); + return PJ_TRUE; + } + + dwStatus = GetLastError(); + if (dwStatus==ERROR_HANDLE_EOF) + return PJ_TRUE; + + return 0; +} +*/ + +PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd, + pj_off_t offset, + enum pj_file_seek_type whence) +{ + DWORD dwMoveMethod; + DWORD dwNewPos; + LONG hi32; + + if (whence == PJ_SEEK_SET) + dwMoveMethod = FILE_BEGIN; + else if (whence == PJ_SEEK_CUR) + dwMoveMethod = FILE_CURRENT; + else if (whence == PJ_SEEK_END) + dwMoveMethod = FILE_END; + else { + pj_assert(!"Invalid whence in file_setpos"); + return PJ_EINVAL; + } + + hi32 = (LONG)(offset >> 32); + dwNewPos = SetFilePointer(fd, (long)offset, &hi32, dwMoveMethod); + if (dwNewPos == (DWORD)INVALID_SET_FILE_POINTER) { + DWORD dwStatus = GetLastError(); + if (dwStatus != 0) + return PJ_RETURN_OS_ERROR(dwStatus); + /* dwNewPos actually is not an error. */ + } + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd, + pj_off_t *pos) +{ + LONG hi32 = 0; + DWORD lo32; + + lo32 = SetFilePointer(fd, 0, &hi32, FILE_CURRENT); + if (lo32 == (DWORD)INVALID_SET_FILE_POINTER) { + DWORD dwStatus = GetLastError(); + if (dwStatus != 0) + return PJ_RETURN_OS_ERROR(dwStatus); + } + + *pos = hi32; + *pos = (*pos << 32) + lo32; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_flush(pj_oshandle_t fd) +{ + BOOL rc; + + rc = FlushFileBuffers(fd); + + if (!rc) { + DWORD dwStatus = GetLastError(); + if (dwStatus != 0) + return PJ_RETURN_OS_ERROR(dwStatus); + } + + return PJ_SUCCESS; +} diff --git a/pjlib/src/pj/guid.c b/pjlib/src/pj/guid.c new file mode 100644 index 0000000..8ad5f78 --- /dev/null +++ b/pjlib/src/pj/guid.c @@ -0,0 +1,27 @@ +/* $Id: guid.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include + +PJ_DEF(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str) +{ + str->ptr = (char*)pj_pool_alloc(pool, PJ_GUID_STRING_LENGTH); + pj_generate_unique_string(str); +} diff --git a/pjlib/src/pj/guid_simple.c b/pjlib/src/pj/guid_simple.c new file mode 100644 index 0000000..8bf8ae9 --- /dev/null +++ b/pjlib/src/pj/guid_simple.c @@ -0,0 +1,81 @@ +/* $Id: guid_simple.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include + +PJ_DEF_DATA(const unsigned) PJ_GUID_STRING_LENGTH=32; + +static char guid_chars[64]; + +PJ_DEF(unsigned) pj_GUID_STRING_LENGTH() +{ + return PJ_GUID_STRING_LENGTH; +} + +static void init_guid_chars(void) +{ + char *p = guid_chars; + unsigned i; + + for (i=0; i<10; ++i) + *p++ = '0'+i; + + for (i=0; i<26; ++i) { + *p++ = 'a'+i; + *p++ = 'A'+i; + } + + *p++ = '-'; + *p++ = '.'; +} + +PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str) +{ + char *p, *end; + + PJ_CHECK_STACK(); + + if (guid_chars[0] == '\0') { + pj_enter_critical_section(); + if (guid_chars[0] == '\0') { + init_guid_chars(); + } + pj_leave_critical_section(); + } + + /* This would only work if PJ_GUID_STRING_LENGTH is multiple of 2 bytes */ + pj_assert(PJ_GUID_STRING_LENGTH % 2 == 0); + + for (p=str->ptr, end=p+PJ_GUID_STRING_LENGTH; p0 && p>=8, rand_val>>=8, p++) { + *p = guid_chars[(rand_val & 0xFF) & 63]; + } + } + + str->slen = PJ_GUID_STRING_LENGTH; + return str; +} + diff --git a/pjlib/src/pj/guid_uuid.c b/pjlib/src/pj/guid_uuid.c new file mode 100644 index 0000000..b850bed --- /dev/null +++ b/pjlib/src/pj/guid_uuid.c @@ -0,0 +1,53 @@ +/* $Id: guid_uuid.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include + +#include + +PJ_DEF_DATA(const unsigned) PJ_GUID_STRING_LENGTH=36; + +PJ_DEF(unsigned) pj_GUID_STRING_LENGTH() +{ + return PJ_GUID_STRING_LENGTH; +} + +PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str) +{ + enum {GUID_LEN = 36}; + char sguid[GUID_LEN + 1]; + uuid_t uuid = {0}; + + PJ_ASSERT_RETURN(GUID_LEN <= PJ_GUID_STRING_LENGTH, NULL); + PJ_ASSERT_RETURN(str->ptr != NULL, NULL); + PJ_CHECK_STACK(); + + uuid_generate(uuid); + uuid_unparse(uuid, sguid); + + pj_memcpy(str->ptr, sguid, GUID_LEN); + str->slen = GUID_LEN; + + return str; +} + diff --git a/pjlib/src/pj/guid_win32.c b/pjlib/src/pj/guid_win32.c new file mode 100644 index 0000000..c40fcad --- /dev/null +++ b/pjlib/src/pj/guid_win32.c @@ -0,0 +1,72 @@ +/* $Id: guid_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include + + +PJ_DEF_DATA(const unsigned) PJ_GUID_STRING_LENGTH=32; + +PJ_DEF(unsigned) pj_GUID_STRING_LENGTH() +{ + return PJ_GUID_STRING_LENGTH; +} + +PJ_INLINE(void) hex2digit(unsigned value, char *p) +{ + static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + *p++ = hex[ (value & 0xF0) >> 4 ]; + *p++ = hex[ (value & 0x0F) ]; +} + +static void guid_to_str( GUID *guid, pj_str_t *str ) +{ + unsigned i; + const unsigned char *src = (const unsigned char*)guid; + char *dst = str->ptr; + + guid->Data1 = pj_ntohl(guid->Data1); + guid->Data2 = pj_ntohs(guid->Data2); + guid->Data3 = pj_ntohs(guid->Data3); + + for (i=0; i<16; ++i) { + hex2digit( *src, dst ); + dst += 2; + ++src; + } + str->slen = 32; +} + + +PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str) +{ + GUID guid; + + PJ_CHECK_STACK(); + + CoCreateGuid(&guid); + guid_to_str( &guid, str ); + return str; +} + diff --git a/pjlib/src/pj/hash.c b/pjlib/src/pj/hash.c new file mode 100644 index 0000000..00f167f --- /dev/null +++ b/pjlib/src/pj/hash.c @@ -0,0 +1,349 @@ +/* $Id: hash.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include + +/** + * The hash multiplier used to calculate hash value. + */ +#define PJ_HASH_MULTIPLIER 33 + + +struct pj_hash_entry +{ + struct pj_hash_entry *next; + void *key; + pj_uint32_t hash; + pj_uint32_t keylen; + void *value; +}; + + +struct pj_hash_table_t +{ + pj_hash_entry **table; + unsigned count, rows; + pj_hash_iterator_t iterator; +}; + + + +PJ_DEF(pj_uint32_t) pj_hash_calc(pj_uint32_t hash, const void *key, + unsigned keylen) +{ + PJ_CHECK_STACK(); + + if (keylen==PJ_HASH_KEY_STRING) { + const pj_uint8_t *p = (const pj_uint8_t*)key; + for ( ; *p; ++p ) { + hash = (hash * PJ_HASH_MULTIPLIER) + *p; + } + } else { + const pj_uint8_t *p = (const pj_uint8_t*)key, + *end = p + keylen; + for ( ; p!=end; ++p) { + hash = (hash * PJ_HASH_MULTIPLIER) + *p; + } + } + return hash; +} + +PJ_DEF(pj_uint32_t) pj_hash_calc_tolower( pj_uint32_t hval, + char *result, + const pj_str_t *key) +{ + long i; + +#if defined(PJ_HASH_USE_OWN_TOLOWER) && PJ_HASH_USE_OWN_TOLOWER != 0 + for (i=0; islen; ++i) { + pj_uint8_t c = key->ptr[i]; + if (c & 64) + result[i] = (char)(c | 32); + else + result[i] = (char)c; + hval = hval * PJ_HASH_MULTIPLIER + result[i]; + } +#else + for (i=0; islen; ++i) { + result[i] = (char)pj_tolower(key->ptr[i]); + hval = hval * PJ_HASH_MULTIPLIER + result[i]; + } +#endif + + return hval; +} + + +PJ_DEF(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size) +{ + pj_hash_table_t *h; + unsigned table_size; + + /* Check that PJ_HASH_ENTRY_BUF_SIZE is correct. */ + PJ_ASSERT_RETURN(sizeof(pj_hash_entry)<=PJ_HASH_ENTRY_BUF_SIZE, NULL); + + h = PJ_POOL_ALLOC_T(pool, pj_hash_table_t); + h->count = 0; + + PJ_LOG( 6, ("hashtbl", "hash table %p created from pool %s", h, pj_pool_getobjname(pool))); + + /* size must be 2^n - 1. + round-up the size to this rule, except when size is 2^n, then size + will be round-down to 2^n-1. + */ + table_size = 8; + do { + table_size <<= 1; + } while (table_size < size); + table_size -= 1; + + h->rows = table_size; + h->table = (pj_hash_entry**) + pj_pool_calloc(pool, table_size+1, sizeof(pj_hash_entry*)); + return h; +} + +static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht, + const void *key, unsigned keylen, + void *val, pj_uint32_t *hval, + void *entry_buf) +{ + pj_uint32_t hash; + pj_hash_entry **p_entry, *entry; + + if (hval && *hval != 0) { + hash = *hval; + if (keylen==PJ_HASH_KEY_STRING) { + keylen = pj_ansi_strlen((const char*)key); + } + } else { + /* This slightly differs with pj_hash_calc() because we need + * to get the keylen when keylen is PJ_HASH_KEY_STRING. + */ + hash=0; + if (keylen==PJ_HASH_KEY_STRING) { + const pj_uint8_t *p = (const pj_uint8_t*)key; + for ( ; *p; ++p ) { + hash = hash * PJ_HASH_MULTIPLIER + *p; + } + keylen = p - (const unsigned char*)key; + } else { + const pj_uint8_t *p = (const pj_uint8_t*)key, + *end = p + keylen; + for ( ; p!=end; ++p) { + hash = hash * PJ_HASH_MULTIPLIER + *p; + } + } + + /* Report back the computed hash. */ + if (hval) + *hval = hash; + } + + /* scan the linked list */ + for (p_entry = &ht->table[hash & ht->rows], entry=*p_entry; + entry; + p_entry = &entry->next, entry = *p_entry) + { + if (entry->hash==hash && entry->keylen==keylen && + pj_memcmp(entry->key, key, keylen)==0) + { + break; + } + } + + if (entry || val==NULL) + return p_entry; + + /* Entry not found, create a new one. + * If entry_buf is specified, use it. Otherwise allocate from pool. + */ + if (entry_buf) { + entry = (pj_hash_entry*)entry_buf; + } else { + /* Pool must be specified! */ + PJ_ASSERT_RETURN(pool != NULL, NULL); + + entry = PJ_POOL_ALLOC_T(pool, pj_hash_entry); + PJ_LOG(6, ("hashtbl", + "%p: New p_entry %p created, pool used=%u, cap=%u", + ht, entry, pj_pool_get_used_size(pool), + pj_pool_get_capacity(pool))); + } + entry->next = NULL; + entry->hash = hash; + if (pool) { + entry->key = pj_pool_alloc(pool, keylen); + pj_memcpy(entry->key, key, keylen); + } else { + entry->key = (void*)key; + } + entry->keylen = keylen; + entry->value = val; + *p_entry = entry; + + ++ht->count; + + return p_entry; +} + +PJ_DEF(void *) pj_hash_get( pj_hash_table_t *ht, + const void *key, unsigned keylen, + pj_uint32_t *hval) +{ + pj_hash_entry *entry; + entry = *find_entry( NULL, ht, key, keylen, NULL, hval, NULL); + return entry ? entry->value : NULL; +} + +PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht, + const void *key, unsigned keylen, pj_uint32_t hval, + void *value ) +{ + pj_hash_entry **p_entry; + + p_entry = find_entry( pool, ht, key, keylen, value, &hval, NULL); + if (*p_entry) { + if (value == NULL) { + /* delete entry */ + PJ_LOG(6, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry)); + *p_entry = (*p_entry)->next; + --ht->count; + + } else { + /* overwrite */ + (*p_entry)->value = value; + PJ_LOG(6, ("hashtbl", "%p: p_entry %p value set to %p", ht, + *p_entry, value)); + } + } +} + +PJ_DEF(void) pj_hash_set_np( pj_hash_table_t *ht, + const void *key, unsigned keylen, + pj_uint32_t hval, pj_hash_entry_buf entry_buf, + void *value) +{ + pj_hash_entry **p_entry; + + p_entry = find_entry( NULL, ht, key, keylen, value, &hval, + (void*)entry_buf ); + if (*p_entry) { + if (value == NULL) { + /* delete entry */ + PJ_LOG(6, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry)); + *p_entry = (*p_entry)->next; + --ht->count; + + } else { + /* overwrite */ + (*p_entry)->value = value; + PJ_LOG(6, ("hashtbl", "%p: p_entry %p value set to %p", ht, + *p_entry, value)); + } + } +} + +PJ_DEF(unsigned) pj_hash_count( pj_hash_table_t *ht ) +{ + return ht->count; +} + +PJ_DEF(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht, + pj_hash_iterator_t *it ) +{ + it->index = 0; + it->entry = NULL; + + for (; it->index <= ht->rows; ++it->index) { + it->entry = ht->table[it->index]; + if (it->entry) { + break; + } + } + + return it->entry ? it : NULL; +} + +PJ_DEF(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht, + pj_hash_iterator_t *it ) +{ + it->entry = it->entry->next; + if (it->entry) { + return it; + } + + for (++it->index; it->index <= ht->rows; ++it->index) { + it->entry = ht->table[it->index]; + if (it->entry) { + break; + } + } + + return it->entry ? it : NULL; +} + +PJ_DEF(void*) pj_hash_this( pj_hash_table_t *ht, pj_hash_iterator_t *it ) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(ht); + return it->entry->value; +} + +#if 0 +void pj_hash_dump_collision( pj_hash_table_t *ht ) +{ + unsigned min=0xFFFFFFFF, max=0; + unsigned i; + char line[120]; + int len, totlen = 0; + + for (i=0; i<=ht->rows; ++i) { + unsigned count = 0; + pj_hash_entry *entry = ht->table[i]; + while (entry) { + ++count; + entry = entry->next; + } + if (count < min) + min = count; + if (count > max) + max = count; + len = pj_snprintf( line+totlen, sizeof(line)-totlen, "%3d:%3d ", i, count); + if (len < 1) + break; + totlen += len; + + if ((i+1) % 10 == 0) { + line[totlen] = '\0'; + PJ_LOG(4,(__FILE__, line)); + } + } + + PJ_LOG(4,(__FILE__,"Count: %d, min: %d, max: %d\n", ht->count, min, max)); +} +#endif + + diff --git a/pjlib/src/pj/ioqueue_common_abs.c b/pjlib/src/pj/ioqueue_common_abs.c new file mode 100644 index 0000000..ee4506d --- /dev/null +++ b/pjlib/src/pj/ioqueue_common_abs.c @@ -0,0 +1,1314 @@ +/* $Id: ioqueue_common_abs.c 3666 2011-07-19 08:40:20Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + */ + +/* + * ioqueue_common_abs.c + * + * This contains common functionalities to emulate proactor pattern with + * various event dispatching mechanisms (e.g. select, epoll). + * + * This file will be included by the appropriate ioqueue implementation. + * This file is NOT supposed to be compiled as stand-alone source. + */ + +#define PENDING_RETRY 2 + +static void ioqueue_init( pj_ioqueue_t *ioqueue ) +{ + ioqueue->lock = NULL; + ioqueue->auto_delete_lock = 0; + ioqueue->default_concurrency = PJ_IOQUEUE_DEFAULT_ALLOW_CONCURRENCY; +} + +static pj_status_t ioqueue_destroy(pj_ioqueue_t *ioqueue) +{ + if (ioqueue->auto_delete_lock && ioqueue->lock ) { + pj_lock_release(ioqueue->lock); + return pj_lock_destroy(ioqueue->lock); + } + + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_set_lock() + */ +PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioqueue, + pj_lock_t *lock, + pj_bool_t auto_delete ) +{ + PJ_ASSERT_RETURN(ioqueue && lock, PJ_EINVAL); + + if (ioqueue->auto_delete_lock && ioqueue->lock) { + pj_lock_destroy(ioqueue->lock); + } + + ioqueue->lock = lock; + ioqueue->auto_delete_lock = auto_delete; + + return PJ_SUCCESS; +} + +static pj_status_t ioqueue_init_key( pj_pool_t *pool, + pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb) +{ + pj_status_t rc; + int optlen; + + PJ_UNUSED_ARG(pool); + + key->ioqueue = ioqueue; + key->fd = sock; + key->user_data = user_data; + pj_list_init(&key->read_list); + pj_list_init(&key->write_list); +#if PJ_HAS_TCP + pj_list_init(&key->accept_list); + key->connecting = 0; +#endif + + /* Save callback. */ + pj_memcpy(&key->cb, cb, sizeof(pj_ioqueue_callback)); + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Set initial reference count to 1 */ + pj_assert(key->ref_count == 0); + ++key->ref_count; + + key->closing = 0; +#endif + + rc = pj_ioqueue_set_concurrency(key, ioqueue->default_concurrency); + if (rc != PJ_SUCCESS) + return rc; + + /* Get socket type. When socket type is datagram, some optimization + * will be performed during send to allow parallel send operations. + */ + optlen = sizeof(key->fd_type); + rc = pj_sock_getsockopt(sock, pj_SOL_SOCKET(), pj_SO_TYPE(), + &key->fd_type, &optlen); + if (rc != PJ_SUCCESS) + key->fd_type = pj_SOCK_STREAM(); + + /* Create mutex for the key. */ +#if !PJ_IOQUEUE_HAS_SAFE_UNREG + rc = pj_mutex_create_simple(pool, NULL, &key->mutex); +#endif + + return rc; +} + +/* + * pj_ioqueue_get_user_data() + * + * Obtain value associated with a key. + */ +PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) +{ + PJ_ASSERT_RETURN(key != NULL, NULL); + return key->user_data; +} + +/* + * pj_ioqueue_set_user_data() + */ +PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key, + void *user_data, + void **old_data) +{ + PJ_ASSERT_RETURN(key, PJ_EINVAL); + + if (old_data) + *old_data = key->user_data; + key->user_data = user_data; + + return PJ_SUCCESS; +} + +PJ_INLINE(int) key_has_pending_write(pj_ioqueue_key_t *key) +{ + return !pj_list_empty(&key->write_list); +} + +PJ_INLINE(int) key_has_pending_read(pj_ioqueue_key_t *key) +{ + return !pj_list_empty(&key->read_list); +} + +PJ_INLINE(int) key_has_pending_accept(pj_ioqueue_key_t *key) +{ +#if PJ_HAS_TCP + return !pj_list_empty(&key->accept_list); +#else + PJ_UNUSED_ARG(key); + return 0; +#endif +} + +PJ_INLINE(int) key_has_pending_connect(pj_ioqueue_key_t *key) +{ + return key->connecting; +} + + +#if PJ_IOQUEUE_HAS_SAFE_UNREG +# define IS_CLOSING(key) (key->closing) +#else +# define IS_CLOSING(key) (0) +#endif + + +/* + * ioqueue_dispatch_event() + * + * Report occurence of an event in the key to be processed by the + * framework. + */ +void ioqueue_dispatch_write_event(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h) +{ + /* Lock the key. */ + pj_mutex_lock(h->mutex); + + if (IS_CLOSING(h)) { + pj_mutex_unlock(h->mutex); + return; + } + +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0 + if (h->connecting) { + /* Completion of connect() operation */ + pj_status_t status; + pj_bool_t has_lock; + + /* Clear operation. */ + h->connecting = 0; + + ioqueue_remove_from_set(ioqueue, h, WRITEABLE_EVENT); + ioqueue_remove_from_set(ioqueue, h, EXCEPTION_EVENT); + + +#if (defined(PJ_HAS_SO_ERROR) && PJ_HAS_SO_ERROR!=0) + /* from connect(2): + * On Linux, use getsockopt to read the SO_ERROR option at + * level SOL_SOCKET to determine whether connect() completed + * successfully (if SO_ERROR is zero). + */ + { + int value; + int vallen = sizeof(value); + int gs_rc = pj_sock_getsockopt(h->fd, SOL_SOCKET, SO_ERROR, + &value, &vallen); + if (gs_rc != 0) { + /* Argh!! What to do now??? + * Just indicate that the socket is connected. The + * application will get error as soon as it tries to use + * the socket to send/receive. + */ + status = PJ_SUCCESS; + } else { + status = PJ_STATUS_FROM_OS(value); + } + } +#elif defined(PJ_WIN32) && PJ_WIN32!=0 + status = PJ_SUCCESS; /* success */ +#else + /* Excellent information in D.J. Bernstein page: + * http://cr.yp.to/docs/connect.html + * + * Seems like the most portable way of detecting connect() + * failure is to call getpeername(). If socket is connected, + * getpeername() will return 0. If the socket is not connected, + * it will return ENOTCONN, and read(fd, &ch, 1) will produce + * the right errno through error slippage. This is a combination + * of suggestions from Douglas C. Schmidt and Ken Keys. + */ + { + struct sockaddr_in addr; + int addrlen = sizeof(addr); + + status = pj_sock_getpeername(h->fd, (struct sockaddr*)&addr, + &addrlen); + } +#endif + + /* Unlock; from this point we don't need to hold key's mutex + * (unless concurrency is disabled, which in this case we should + * hold the mutex while calling the callback) */ + if (h->allow_concurrent) { + /* concurrency may be changed while we're in the callback, so + * save it to a flag. + */ + has_lock = PJ_FALSE; + pj_mutex_unlock(h->mutex); + } else { + has_lock = PJ_TRUE; + } + + /* Call callback. */ + if (h->cb.on_connect_complete && !IS_CLOSING(h)) + (*h->cb.on_connect_complete)(h, status); + + /* Unlock if we still hold the lock */ + if (has_lock) { + pj_mutex_unlock(h->mutex); + } + + /* Done. */ + + } else +#endif /* PJ_HAS_TCP */ + if (key_has_pending_write(h)) { + /* Socket is writable. */ + struct write_operation *write_op; + pj_ssize_t sent; + pj_status_t send_rc = PJ_SUCCESS; + + /* Get the first in the queue. */ + write_op = h->write_list.next; + + /* For datagrams, we can remove the write_op from the list + * so that send() can work in parallel. + */ + if (h->fd_type == pj_SOCK_DGRAM()) { + pj_list_erase(write_op); + + if (pj_list_empty(&h->write_list)) + ioqueue_remove_from_set(ioqueue, h, WRITEABLE_EVENT); + + } + + /* Send the data. + * Unfortunately we must do this while holding key's mutex, thus + * preventing parallel write on a single key.. :-(( + */ + sent = write_op->size - write_op->written; + if (write_op->op == PJ_IOQUEUE_OP_SEND) { + send_rc = pj_sock_send(h->fd, write_op->buf+write_op->written, + &sent, write_op->flags); + /* Can't do this. We only clear "op" after we're finished sending + * the whole buffer. + */ + //write_op->op = 0; + } else if (write_op->op == PJ_IOQUEUE_OP_SEND_TO) { + int retry = 2; + while (--retry >= 0) { + send_rc = pj_sock_sendto(h->fd, + write_op->buf+write_op->written, + &sent, write_op->flags, + &write_op->rmt_addr, + write_op->rmt_addrlen); +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 + /* Special treatment for dead UDP sockets here, see ticket #1107 */ + if (send_rc==PJ_STATUS_FROM_OS(EPIPE) && !IS_CLOSING(h) && + h->fd_type==pj_SOCK_DGRAM()) + { + PJ_PERROR(4,(THIS_FILE, send_rc, + "Send error for socket %d, retrying", + h->fd)); + replace_udp_sock(h); + continue; + } +#endif + break; + } + + /* Can't do this. We only clear "op" after we're finished sending + * the whole buffer. + */ + //write_op->op = 0; + } else { + pj_assert(!"Invalid operation type!"); + write_op->op = PJ_IOQUEUE_OP_NONE; + send_rc = PJ_EBUG; + } + + if (send_rc == PJ_SUCCESS) { + write_op->written += sent; + } else { + pj_assert(send_rc > 0); + write_op->written = -send_rc; + } + + /* Are we finished with this buffer? */ + if (send_rc!=PJ_SUCCESS || + write_op->written == (pj_ssize_t)write_op->size || + h->fd_type == pj_SOCK_DGRAM()) + { + pj_bool_t has_lock; + + write_op->op = PJ_IOQUEUE_OP_NONE; + + if (h->fd_type != pj_SOCK_DGRAM()) { + /* Write completion of the whole stream. */ + pj_list_erase(write_op); + + /* Clear operation if there's no more data to send. */ + if (pj_list_empty(&h->write_list)) + ioqueue_remove_from_set(ioqueue, h, WRITEABLE_EVENT); + + } + + /* Unlock; from this point we don't need to hold key's mutex + * (unless concurrency is disabled, which in this case we should + * hold the mutex while calling the callback) */ + if (h->allow_concurrent) { + /* concurrency may be changed while we're in the callback, so + * save it to a flag. + */ + has_lock = PJ_FALSE; + pj_mutex_unlock(h->mutex); + } else { + has_lock = PJ_TRUE; + } + + /* Call callback. */ + if (h->cb.on_write_complete && !IS_CLOSING(h)) { + (*h->cb.on_write_complete)(h, + (pj_ioqueue_op_key_t*)write_op, + write_op->written); + } + + if (has_lock) { + pj_mutex_unlock(h->mutex); + } + + } else { + pj_mutex_unlock(h->mutex); + } + + /* Done. */ + } else { + /* + * This is normal; execution may fall here when multiple threads + * are signalled for the same event, but only one thread eventually + * able to process the event. + */ + pj_mutex_unlock(h->mutex); + } +} + +void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h ) +{ + pj_status_t rc; + + /* Lock the key. */ + pj_mutex_lock(h->mutex); + + if (IS_CLOSING(h)) { + pj_mutex_unlock(h->mutex); + return; + } + +# if PJ_HAS_TCP + if (!pj_list_empty(&h->accept_list)) { + + struct accept_operation *accept_op; + pj_bool_t has_lock; + + /* Get one accept operation from the list. */ + accept_op = h->accept_list.next; + pj_list_erase(accept_op); + accept_op->op = PJ_IOQUEUE_OP_NONE; + + /* Clear bit in fdset if there is no more pending accept */ + if (pj_list_empty(&h->accept_list)) + ioqueue_remove_from_set(ioqueue, h, READABLE_EVENT); + + rc=pj_sock_accept(h->fd, accept_op->accept_fd, + accept_op->rmt_addr, accept_op->addrlen); + if (rc==PJ_SUCCESS && accept_op->local_addr) { + rc = pj_sock_getsockname(*accept_op->accept_fd, + accept_op->local_addr, + accept_op->addrlen); + } + + /* Unlock; from this point we don't need to hold key's mutex + * (unless concurrency is disabled, which in this case we should + * hold the mutex while calling the callback) */ + if (h->allow_concurrent) { + /* concurrency may be changed while we're in the callback, so + * save it to a flag. + */ + has_lock = PJ_FALSE; + pj_mutex_unlock(h->mutex); + } else { + has_lock = PJ_TRUE; + } + + /* Call callback. */ + if (h->cb.on_accept_complete && !IS_CLOSING(h)) { + (*h->cb.on_accept_complete)(h, + (pj_ioqueue_op_key_t*)accept_op, + *accept_op->accept_fd, rc); + } + + if (has_lock) { + pj_mutex_unlock(h->mutex); + } + } + else +# endif + if (key_has_pending_read(h)) { + struct read_operation *read_op; + pj_ssize_t bytes_read; + pj_bool_t has_lock; + + /* Get one pending read operation from the list. */ + read_op = h->read_list.next; + pj_list_erase(read_op); + + /* Clear fdset if there is no pending read. */ + if (pj_list_empty(&h->read_list)) + ioqueue_remove_from_set(ioqueue, h, READABLE_EVENT); + + bytes_read = read_op->size; + + if ((read_op->op == PJ_IOQUEUE_OP_RECV_FROM)) { + read_op->op = PJ_IOQUEUE_OP_NONE; + rc = pj_sock_recvfrom(h->fd, read_op->buf, &bytes_read, + read_op->flags, + read_op->rmt_addr, + read_op->rmt_addrlen); + } else if ((read_op->op == PJ_IOQUEUE_OP_RECV)) { + read_op->op = PJ_IOQUEUE_OP_NONE; + rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, + read_op->flags); + } else { + pj_assert(read_op->op == PJ_IOQUEUE_OP_READ); + read_op->op = PJ_IOQUEUE_OP_NONE; + /* + * User has specified pj_ioqueue_read(). + * On Win32, we should do ReadFile(). But because we got + * here because of select() anyway, user must have put a + * socket descriptor on h->fd, which in this case we can + * just call pj_sock_recv() instead of ReadFile(). + * On Unix, user may put a file in h->fd, so we'll have + * to call read() here. + * This may not compile on systems which doesn't have + * read(). That's why we only specify PJ_LINUX here so + * that error is easier to catch. + */ +# if defined(PJ_WIN32) && PJ_WIN32 != 0 || \ + defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0 + rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, + read_op->flags); + //rc = ReadFile((HANDLE)h->fd, read_op->buf, read_op->size, + // &bytes_read, NULL); +# elif (defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0) + bytes_read = read(h->fd, read_op->buf, bytes_read); + rc = (bytes_read >= 0) ? PJ_SUCCESS : pj_get_os_error(); +# elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 + bytes_read = sys_read(h->fd, read_op->buf, bytes_read); + rc = (bytes_read >= 0) ? PJ_SUCCESS : -bytes_read; +# else +# error "Implement read() for this platform!" +# endif + } + + if (rc != PJ_SUCCESS) { +# if defined(PJ_WIN32) && PJ_WIN32 != 0 + /* On Win32, for UDP, WSAECONNRESET on the receive side + * indicates that previous sending has triggered ICMP Port + * Unreachable message. + * But we wouldn't know at this point which one of previous + * key that has triggered the error, since UDP socket can + * be shared! + * So we'll just ignore it! + */ + + if (rc == PJ_STATUS_FROM_OS(WSAECONNRESET)) { + //PJ_LOG(4,(THIS_FILE, + // "Ignored ICMP port unreach. on key=%p", h)); + } +# endif + + /* In any case we would report this to caller. */ + bytes_read = -rc; + +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 + /* Special treatment for dead UDP sockets here, see ticket #1107 */ + if (rc == PJ_STATUS_FROM_OS(ENOTCONN) && !IS_CLOSING(h) && + h->fd_type==pj_SOCK_DGRAM()) + { + replace_udp_sock(h); + } +#endif + } + + /* Unlock; from this point we don't need to hold key's mutex + * (unless concurrency is disabled, which in this case we should + * hold the mutex while calling the callback) */ + if (h->allow_concurrent) { + /* concurrency may be changed while we're in the callback, so + * save it to a flag. + */ + has_lock = PJ_FALSE; + pj_mutex_unlock(h->mutex); + } else { + has_lock = PJ_TRUE; + } + + /* Call callback. */ + if (h->cb.on_read_complete && !IS_CLOSING(h)) { + (*h->cb.on_read_complete)(h, + (pj_ioqueue_op_key_t*)read_op, + bytes_read); + } + + if (has_lock) { + pj_mutex_unlock(h->mutex); + } + + } else { + /* + * This is normal; execution may fall here when multiple threads + * are signalled for the same event, but only one thread eventually + * able to process the event. + */ + pj_mutex_unlock(h->mutex); + } +} + + +void ioqueue_dispatch_exception_event( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *h ) +{ + pj_bool_t has_lock; + + pj_mutex_lock(h->mutex); + + if (!h->connecting) { + /* It is possible that more than one thread was woken up, thus + * the remaining thread will see h->connecting as zero because + * it has been processed by other thread. + */ + pj_mutex_unlock(h->mutex); + return; + } + + if (IS_CLOSING(h)) { + pj_mutex_unlock(h->mutex); + return; + } + + /* Clear operation. */ + h->connecting = 0; + + ioqueue_remove_from_set(ioqueue, h, WRITEABLE_EVENT); + ioqueue_remove_from_set(ioqueue, h, EXCEPTION_EVENT); + + /* Unlock; from this point we don't need to hold key's mutex + * (unless concurrency is disabled, which in this case we should + * hold the mutex while calling the callback) */ + if (h->allow_concurrent) { + /* concurrency may be changed while we're in the callback, so + * save it to a flag. + */ + has_lock = PJ_FALSE; + pj_mutex_unlock(h->mutex); + } else { + has_lock = PJ_TRUE; + } + + /* Call callback. */ + if (h->cb.on_connect_complete && !IS_CLOSING(h)) { + pj_status_t status = -1; +#if (defined(PJ_HAS_SO_ERROR) && PJ_HAS_SO_ERROR!=0) + int value; + int vallen = sizeof(value); + int gs_rc = pj_sock_getsockopt(h->fd, SOL_SOCKET, SO_ERROR, + &value, &vallen); + if (gs_rc == 0) { + status = PJ_RETURN_OS_ERROR(value); + } +#endif + + (*h->cb.on_connect_complete)(h, status); + } + + if (has_lock) { + pj_mutex_unlock(h->mutex); + } +} + +/* + * pj_ioqueue_recv() + * + * Start asynchronous recv() from the socket. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + void *buffer, + pj_ssize_t *length, + unsigned flags ) +{ + struct read_operation *read_op; + + PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL); + PJ_CHECK_STACK(); + + /* Check if key is closing (need to do this first before accessing + * other variables, since they might have been destroyed. See ticket + * #469). + */ + if (IS_CLOSING(key)) + return PJ_ECANCELLED; + + read_op = (struct read_operation*)op_key; + read_op->op = PJ_IOQUEUE_OP_NONE; + + /* Try to see if there's data immediately available. + */ + if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) { + pj_status_t status; + pj_ssize_t size; + + size = *length; + status = pj_sock_recv(key->fd, buffer, &size, flags); + if (status == PJ_SUCCESS) { + /* Yes! Data is available! */ + *length = size; + return PJ_SUCCESS; + } else { + /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report + * the error to caller. + */ + if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) + return status; + } + } + + flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); + + /* + * No data is immediately available. + * Must schedule asynchronous operation to the ioqueue. + */ + read_op->op = PJ_IOQUEUE_OP_RECV; + read_op->buf = buffer; + read_op->size = *length; + read_op->flags = flags; + + pj_mutex_lock(key->mutex); + /* Check again. Handle may have been closed after the previous check + * in multithreaded app. If we add bad handle to the set it will + * corrupt the ioqueue set. See #913 + */ + if (IS_CLOSING(key)) { + pj_mutex_unlock(key->mutex); + return PJ_ECANCELLED; + } + pj_list_insert_before(&key->read_list, read_op); + ioqueue_add_to_set(key->ioqueue, key, READABLE_EVENT); + pj_mutex_unlock(key->mutex); + + return PJ_EPENDING; +} + +/* + * pj_ioqueue_recvfrom() + * + * Start asynchronous recvfrom() from the socket. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + void *buffer, + pj_ssize_t *length, + unsigned flags, + pj_sockaddr_t *addr, + int *addrlen) +{ + struct read_operation *read_op; + + PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL); + PJ_CHECK_STACK(); + + /* Check if key is closing. */ + if (IS_CLOSING(key)) + return PJ_ECANCELLED; + + read_op = (struct read_operation*)op_key; + read_op->op = PJ_IOQUEUE_OP_NONE; + + /* Try to see if there's data immediately available. + */ + if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) { + pj_status_t status; + pj_ssize_t size; + + size = *length; + status = pj_sock_recvfrom(key->fd, buffer, &size, flags, + addr, addrlen); + if (status == PJ_SUCCESS) { + /* Yes! Data is available! */ + *length = size; + return PJ_SUCCESS; + } else { + /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report + * the error to caller. + */ + if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) + return status; + } + } + + flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); + + /* + * No data is immediately available. + * Must schedule asynchronous operation to the ioqueue. + */ + read_op->op = PJ_IOQUEUE_OP_RECV_FROM; + read_op->buf = buffer; + read_op->size = *length; + read_op->flags = flags; + read_op->rmt_addr = addr; + read_op->rmt_addrlen = addrlen; + + pj_mutex_lock(key->mutex); + /* Check again. Handle may have been closed after the previous check + * in multithreaded app. If we add bad handle to the set it will + * corrupt the ioqueue set. See #913 + */ + if (IS_CLOSING(key)) { + pj_mutex_unlock(key->mutex); + return PJ_ECANCELLED; + } + pj_list_insert_before(&key->read_list, read_op); + ioqueue_add_to_set(key->ioqueue, key, READABLE_EVENT); + pj_mutex_unlock(key->mutex); + + return PJ_EPENDING; +} + +/* + * pj_ioqueue_send() + * + * Start asynchronous send() to the descriptor. + */ +PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + const void *data, + pj_ssize_t *length, + unsigned flags) +{ + struct write_operation *write_op; + pj_status_t status; + unsigned retry; + pj_ssize_t sent; + + PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL); + PJ_CHECK_STACK(); + + /* Check if key is closing. */ + if (IS_CLOSING(key)) + return PJ_ECANCELLED; + + /* We can not use PJ_IOQUEUE_ALWAYS_ASYNC for socket write. */ + flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); + + /* Fast track: + * Try to send data immediately, only if there's no pending write! + * Note: + * We are speculating that the list is empty here without properly + * acquiring ioqueue's mutex first. This is intentional, to maximize + * performance via parallelism. + * + * This should be safe, because: + * - by convention, we require caller to make sure that the + * key is not unregistered while other threads are invoking + * an operation on the same key. + * - pj_list_empty() is safe to be invoked by multiple threads, + * even when other threads are modifying the list. + */ + if (pj_list_empty(&key->write_list)) { + /* + * See if data can be sent immediately. + */ + sent = *length; + status = pj_sock_send(key->fd, data, &sent, flags); + if (status == PJ_SUCCESS) { + /* Success! */ + *length = sent; + return PJ_SUCCESS; + } else { + /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report + * the error to caller. + */ + if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) { + return status; + } + } + } + + /* + * Schedule asynchronous send. + */ + write_op = (struct write_operation*)op_key; + + /* Spin if write_op has pending operation */ + for (retry=0; write_op->op != 0 && retryop) { + /* Unable to send packet because there is already pending write in the + * write_op. We could not put the operation into the write_op + * because write_op already contains a pending operation! And + * we could not send the packet directly with send() either, + * because that will break the order of the packet. So we can + * only return error here. + * + * This could happen for example in multithreads program, + * where polling is done by one thread, while other threads are doing + * the sending only. If the polling thread runs on lower priority + * than the sending thread, then it's possible that the pending + * write flag is not cleared in-time because clearing is only done + * during polling. + * + * Aplication should specify multiple write operation keys on + * situation like this. + */ + //pj_assert(!"ioqueue: there is pending operation on this key!"); + return PJ_EBUSY; + } + + write_op->op = PJ_IOQUEUE_OP_SEND; + write_op->buf = (char*)data; + write_op->size = *length; + write_op->written = 0; + write_op->flags = flags; + + pj_mutex_lock(key->mutex); + /* Check again. Handle may have been closed after the previous check + * in multithreaded app. If we add bad handle to the set it will + * corrupt the ioqueue set. See #913 + */ + if (IS_CLOSING(key)) { + pj_mutex_unlock(key->mutex); + return PJ_ECANCELLED; + } + pj_list_insert_before(&key->write_list, write_op); + ioqueue_add_to_set(key->ioqueue, key, WRITEABLE_EVENT); + pj_mutex_unlock(key->mutex); + + return PJ_EPENDING; +} + + +/* + * pj_ioqueue_sendto() + * + * Start asynchronous write() to the descriptor. + */ +PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + const void *data, + pj_ssize_t *length, + pj_uint32_t flags, + const pj_sockaddr_t *addr, + int addrlen) +{ + struct write_operation *write_op; + unsigned retry; + pj_bool_t restart_retry = PJ_FALSE; + pj_status_t status; + pj_ssize_t sent; + + PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL); + PJ_CHECK_STACK(); + +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 +retry_on_restart: +#else + PJ_UNUSED_ARG(restart_retry); +#endif + /* Check if key is closing. */ + if (IS_CLOSING(key)) + return PJ_ECANCELLED; + + /* We can not use PJ_IOQUEUE_ALWAYS_ASYNC for socket write */ + flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); + + /* Fast track: + * Try to send data immediately, only if there's no pending write! + * Note: + * We are speculating that the list is empty here without properly + * acquiring ioqueue's mutex first. This is intentional, to maximize + * performance via parallelism. + * + * This should be safe, because: + * - by convention, we require caller to make sure that the + * key is not unregistered while other threads are invoking + * an operation on the same key. + * - pj_list_empty() is safe to be invoked by multiple threads, + * even when other threads are modifying the list. + */ + if (pj_list_empty(&key->write_list)) { + /* + * See if data can be sent immediately. + */ + sent = *length; + status = pj_sock_sendto(key->fd, data, &sent, flags, addr, addrlen); + if (status == PJ_SUCCESS) { + /* Success! */ + *length = sent; + return PJ_SUCCESS; + } else { + /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report + * the error to caller. + */ + if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) { +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 + /* Special treatment for dead UDP sockets here, see ticket #1107 */ + if (status==PJ_STATUS_FROM_OS(EPIPE) && !IS_CLOSING(key) && + key->fd_type==pj_SOCK_DGRAM() && !restart_retry) + { + PJ_PERROR(4,(THIS_FILE, status, + "Send error for socket %d, retrying", + key->fd)); + replace_udp_sock(key); + restart_retry = PJ_TRUE; + goto retry_on_restart; + } +#endif + + return status; + } + status = status; + } + } + + /* + * Check that address storage can hold the address parameter. + */ + PJ_ASSERT_RETURN(addrlen <= (int)sizeof(pj_sockaddr_in), PJ_EBUG); + + /* + * Schedule asynchronous send. + */ + write_op = (struct write_operation*)op_key; + + /* Spin if write_op has pending operation */ + for (retry=0; write_op->op != 0 && retryop) { + /* Unable to send packet because there is already pending write on the + * write_op. We could not put the operation into the write_op + * because write_op already contains a pending operation! And + * we could not send the packet directly with sendto() either, + * because that will break the order of the packet. So we can + * only return error here. + * + * This could happen for example in multithreads program, + * where polling is done by one thread, while other threads are doing + * the sending only. If the polling thread runs on lower priority + * than the sending thread, then it's possible that the pending + * write flag is not cleared in-time because clearing is only done + * during polling. + * + * Aplication should specify multiple write operation keys on + * situation like this. + */ + //pj_assert(!"ioqueue: there is pending operation on this key!"); + return PJ_EBUSY; + } + + write_op->op = PJ_IOQUEUE_OP_SEND_TO; + write_op->buf = (char*)data; + write_op->size = *length; + write_op->written = 0; + write_op->flags = flags; + pj_memcpy(&write_op->rmt_addr, addr, addrlen); + write_op->rmt_addrlen = addrlen; + + pj_mutex_lock(key->mutex); + /* Check again. Handle may have been closed after the previous check + * in multithreaded app. If we add bad handle to the set it will + * corrupt the ioqueue set. See #913 + */ + if (IS_CLOSING(key)) { + pj_mutex_unlock(key->mutex); + return PJ_ECANCELLED; + } + pj_list_insert_before(&key->write_list, write_op); + ioqueue_add_to_set(key->ioqueue, key, WRITEABLE_EVENT); + pj_mutex_unlock(key->mutex); + + return PJ_EPENDING; +} + +#if PJ_HAS_TCP +/* + * Initiate overlapped accept() operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen) +{ + struct accept_operation *accept_op; + pj_status_t status; + + /* check parameters. All must be specified! */ + PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL); + + /* Check if key is closing. */ + if (IS_CLOSING(key)) + return PJ_ECANCELLED; + + accept_op = (struct accept_operation*)op_key; + accept_op->op = PJ_IOQUEUE_OP_NONE; + + /* Fast track: + * See if there's new connection available immediately. + */ + if (pj_list_empty(&key->accept_list)) { + status = pj_sock_accept(key->fd, new_sock, remote, addrlen); + if (status == PJ_SUCCESS) { + /* Yes! New connection is available! */ + if (local && addrlen) { + status = pj_sock_getsockname(*new_sock, local, addrlen); + if (status != PJ_SUCCESS) { + pj_sock_close(*new_sock); + *new_sock = PJ_INVALID_SOCKET; + return status; + } + } + return PJ_SUCCESS; + } else { + /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report + * the error to caller. + */ + if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) { + return status; + } + } + } + + /* + * No connection is available immediately. + * Schedule accept() operation to be completed when there is incoming + * connection available. + */ + accept_op->op = PJ_IOQUEUE_OP_ACCEPT; + accept_op->accept_fd = new_sock; + accept_op->rmt_addr = remote; + accept_op->addrlen= addrlen; + accept_op->local_addr = local; + + pj_mutex_lock(key->mutex); + /* Check again. Handle may have been closed after the previous check + * in multithreaded app. If we add bad handle to the set it will + * corrupt the ioqueue set. See #913 + */ + if (IS_CLOSING(key)) { + pj_mutex_unlock(key->mutex); + return PJ_ECANCELLED; + } + pj_list_insert_before(&key->accept_list, accept_op); + ioqueue_add_to_set(key->ioqueue, key, READABLE_EVENT); + pj_mutex_unlock(key->mutex); + + return PJ_EPENDING; +} + +/* + * Initiate overlapped connect() operation (well, it's non-blocking actually, + * since there's no overlapped version of connect()). + */ +PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ) +{ + pj_status_t status; + + /* check parameters. All must be specified! */ + PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL); + + /* Check if key is closing. */ + if (IS_CLOSING(key)) + return PJ_ECANCELLED; + + /* Check if socket has not been marked for connecting */ + if (key->connecting != 0) + return PJ_EPENDING; + + status = pj_sock_connect(key->fd, addr, addrlen); + if (status == PJ_SUCCESS) { + /* Connected! */ + return PJ_SUCCESS; + } else { + if (status == PJ_STATUS_FROM_OS(PJ_BLOCKING_CONNECT_ERROR_VAL)) { + /* Pending! */ + pj_mutex_lock(key->mutex); + /* Check again. Handle may have been closed after the previous + * check in multithreaded app. See #913 + */ + if (IS_CLOSING(key)) { + pj_mutex_unlock(key->mutex); + return PJ_ECANCELLED; + } + key->connecting = PJ_TRUE; + ioqueue_add_to_set(key->ioqueue, key, WRITEABLE_EVENT); + ioqueue_add_to_set(key->ioqueue, key, EXCEPTION_EVENT); + pj_mutex_unlock(key->mutex); + return PJ_EPENDING; + } else { + /* Error! */ + return status; + } + } +} +#endif /* PJ_HAS_TCP */ + + +PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key, + pj_size_t size ) +{ + pj_bzero(op_key, size); +} + + +/* + * pj_ioqueue_is_pending() + */ +PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key ) +{ + struct generic_operation *op_rec; + + PJ_UNUSED_ARG(key); + + op_rec = (struct generic_operation*)op_key; + return op_rec->op != 0; +} + + +/* + * pj_ioqueue_post_completion() + */ +PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_status ) +{ + struct generic_operation *op_rec; + + /* + * Find the operation key in all pending operation list to + * really make sure that it's still there; then call the callback. + */ + pj_mutex_lock(key->mutex); + + /* Find the operation in the pending read list. */ + op_rec = (struct generic_operation*)key->read_list.next; + while (op_rec != (void*)&key->read_list) { + if (op_rec == (void*)op_key) { + pj_list_erase(op_rec); + op_rec->op = PJ_IOQUEUE_OP_NONE; + pj_mutex_unlock(key->mutex); + + (*key->cb.on_read_complete)(key, op_key, bytes_status); + return PJ_SUCCESS; + } + op_rec = op_rec->next; + } + + /* Find the operation in the pending write list. */ + op_rec = (struct generic_operation*)key->write_list.next; + while (op_rec != (void*)&key->write_list) { + if (op_rec == (void*)op_key) { + pj_list_erase(op_rec); + op_rec->op = PJ_IOQUEUE_OP_NONE; + pj_mutex_unlock(key->mutex); + + (*key->cb.on_write_complete)(key, op_key, bytes_status); + return PJ_SUCCESS; + } + op_rec = op_rec->next; + } + + /* Find the operation in the pending accept list. */ + op_rec = (struct generic_operation*)key->accept_list.next; + while (op_rec != (void*)&key->accept_list) { + if (op_rec == (void*)op_key) { + pj_list_erase(op_rec); + op_rec->op = PJ_IOQUEUE_OP_NONE; + pj_mutex_unlock(key->mutex); + + (*key->cb.on_accept_complete)(key, op_key, + PJ_INVALID_SOCKET, + bytes_status); + return PJ_SUCCESS; + } + op_rec = op_rec->next; + } + + pj_mutex_unlock(key->mutex); + + return PJ_EINVALIDOP; +} + +PJ_DEF(pj_status_t) pj_ioqueue_set_default_concurrency( pj_ioqueue_t *ioqueue, + pj_bool_t allow) +{ + PJ_ASSERT_RETURN(ioqueue != NULL, PJ_EINVAL); + ioqueue->default_concurrency = allow; + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_ioqueue_set_concurrency(pj_ioqueue_key_t *key, + pj_bool_t allow) +{ + PJ_ASSERT_RETURN(key, PJ_EINVAL); + + /* PJ_IOQUEUE_HAS_SAFE_UNREG must be enabled if concurrency is + * disabled. + */ + PJ_ASSERT_RETURN(allow || PJ_IOQUEUE_HAS_SAFE_UNREG, PJ_EINVAL); + + key->allow_concurrent = allow; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_ioqueue_lock_key(pj_ioqueue_key_t *key) +{ + return pj_mutex_lock(key->mutex); +} + +PJ_DEF(pj_status_t) pj_ioqueue_unlock_key(pj_ioqueue_key_t *key) +{ + return pj_mutex_unlock(key->mutex); +} + diff --git a/pjlib/src/pj/ioqueue_common_abs.h b/pjlib/src/pj/ioqueue_common_abs.h new file mode 100644 index 0000000..3a41051 --- /dev/null +++ b/pjlib/src/pj/ioqueue_common_abs.h @@ -0,0 +1,139 @@ +/* $Id */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + */ + +/* ioqueue_common_abs.h + * + * This file contains private declarations for abstracting various + * event polling/dispatching mechanisms (e.g. select, poll, epoll) + * to the ioqueue. + */ + +#include + +/* + * The select ioqueue relies on socket functions (pj_sock_xxx()) to return + * the correct error code. + */ +#if PJ_RETURN_OS_ERROR(100) != PJ_STATUS_FROM_OS(100) +# error "Proper error reporting must be enabled for ioqueue to work!" +#endif + + +struct generic_operation +{ + PJ_DECL_LIST_MEMBER(struct generic_operation); + pj_ioqueue_operation_e op; +}; + +struct read_operation +{ + PJ_DECL_LIST_MEMBER(struct read_operation); + pj_ioqueue_operation_e op; + + void *buf; + pj_size_t size; + unsigned flags; + pj_sockaddr_t *rmt_addr; + int *rmt_addrlen; +}; + +struct write_operation +{ + PJ_DECL_LIST_MEMBER(struct write_operation); + pj_ioqueue_operation_e op; + + char *buf; + pj_size_t size; + pj_ssize_t written; + unsigned flags; + pj_sockaddr_in rmt_addr; + int rmt_addrlen; +}; + +struct accept_operation +{ + PJ_DECL_LIST_MEMBER(struct accept_operation); + pj_ioqueue_operation_e op; + + pj_sock_t *accept_fd; + pj_sockaddr_t *local_addr; + pj_sockaddr_t *rmt_addr; + int *addrlen; +}; + +union operation_key +{ + struct generic_operation generic; + struct read_operation read; + struct write_operation write; +#if PJ_HAS_TCP + struct accept_operation accept; +#endif +}; + +#if PJ_IOQUEUE_HAS_SAFE_UNREG +# define UNREG_FIELDS \ + unsigned ref_count; \ + pj_bool_t closing; \ + pj_time_val free_time; \ + +#else +# define UNREG_FIELDS +#endif + +#define DECLARE_COMMON_KEY \ + PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t); \ + pj_ioqueue_t *ioqueue; \ + pj_mutex_t *mutex; \ + pj_bool_t inside_callback; \ + pj_bool_t destroy_requested; \ + pj_bool_t allow_concurrent; \ + pj_sock_t fd; \ + int fd_type; \ + void *user_data; \ + pj_ioqueue_callback cb; \ + int connecting; \ + struct read_operation read_list; \ + struct write_operation write_list; \ + struct accept_operation accept_list; \ + UNREG_FIELDS + + +#define DECLARE_COMMON_IOQUEUE \ + pj_lock_t *lock; \ + pj_bool_t auto_delete_lock; \ + pj_bool_t default_concurrency; + + +enum ioqueue_event_type +{ + NO_EVENT, + READABLE_EVENT, + WRITEABLE_EVENT, + EXCEPTION_EVENT, +}; + +static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + enum ioqueue_event_type event_type ); +static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + enum ioqueue_event_type event_type); + diff --git a/pjlib/src/pj/ioqueue_dummy.c b/pjlib/src/pj/ioqueue_dummy.c new file mode 100644 index 0000000..6ea5bb7 --- /dev/null +++ b/pjlib/src/pj/ioqueue_dummy.c @@ -0,0 +1,194 @@ +/* $Id: ioqueue_dummy.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define THIS_FILE "ioqueue" + +#define PJ_IOQUEUE_IS_READ_OP(op) \ + ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM)) +#define PJ_IOQUEUE_IS_WRITE_OP(op) \ + ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO)) + + +#if PJ_HAS_TCP +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT) +# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT) +#else +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0 +# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0 +#endif + +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 +# define VALIDATE_FD_SET 1 +#else +# define VALIDATE_FD_SET 0 +#endif + +struct pj_ioqueue_key_t +{ + PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t) + pj_sock_t fd; + pj_ioqueue_operation_e op; + void *user_data; + pj_ioqueue_callback cb; +}; + +struct pj_ioqueue_t +{ +}; + +PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + int max_threads, + pj_ioqueue_t **ptr_ioqueue) +{ + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque) +{ + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, + pj_lock_t *lock, + pj_bool_t auto_delete ) +{ + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioque, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **ptr_key) +{ + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key) +{ + return PJ_ENOTSUP; +} + +PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) +{ + return NULL; +} + + +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + unsigned flags) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + unsigned flags, + pj_sockaddr_t *addr, + int *addrlen) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + unsigned flags) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + unsigned flags, + const pj_sockaddr_t *addr, + int addrlen) +{ + return -1; +} + +#if PJ_HAS_TCP +/* + * Initiate overlapped accept() operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen) +{ + return -1; +} + +/* + * Initiate overlapped connect() operation (well, it's non-blocking actually, + * since there's no overlapped version of connect()). + */ +PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ) +{ + return -1; +} +#endif /* PJ_HAS_TCP */ + diff --git a/pjlib/src/pj/ioqueue_epoll.c b/pjlib/src/pj/ioqueue_epoll.c new file mode 100644 index 0000000..27845f5 --- /dev/null +++ b/pjlib/src/pj/ioqueue_epoll.c @@ -0,0 +1,749 @@ +/* $Id: ioqueue_epoll.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + */ +/* + * ioqueue_epoll.c + * + * This is the implementation of IOQueue framework using /dev/epoll + * API in _both_ Linux user-mode and kernel-mode. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(PJ_LINUX_KERNEL) || PJ_LINUX_KERNEL==0 + /* + * Linux user mode + */ +# include +# include +# include + +# define epoll_data data.ptr +# define epoll_data_type void* +# define ioctl_val_type unsigned long +# define getsockopt_val_ptr int* +# define os_getsockopt getsockopt +# define os_ioctl ioctl +# define os_read read +# define os_close close +# define os_epoll_create epoll_create +# define os_epoll_ctl epoll_ctl +# define os_epoll_wait epoll_wait +#else + /* + * Linux kernel mode. + */ +# include +# include +# if defined(MODVERSIONS) +# include +# endif +# include +# include +# include +# include +# include +# include +# include + enum EPOLL_EVENTS + { + EPOLLIN = 0x001, + EPOLLOUT = 0x004, + EPOLLERR = 0x008, + }; +# define os_epoll_create sys_epoll_create + static int os_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) + { + long rc; + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + rc = sys_epoll_ctl(epfd, op, fd, event); + set_fs(oldfs); + if (rc) { + errno = -rc; + return -1; + } else { + return 0; + } + } + static int os_epoll_wait(int epfd, struct epoll_event *events, + int maxevents, int timeout) + { + int count; + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + count = sys_epoll_wait(epfd, events, maxevents, timeout); + set_fs(oldfs); + return count; + } +# define os_close sys_close +# define os_getsockopt pj_sock_getsockopt + static int os_read(int fd, void *buf, size_t len) + { + long rc; + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + rc = sys_read(fd, buf, len); + set_fs(oldfs); + if (rc) { + errno = -rc; + return -1; + } else { + return 0; + } + } +# define socklen_t unsigned +# define ioctl_val_type unsigned long + int ioctl(int fd, int opt, ioctl_val_type value); + static int os_ioctl(int fd, int opt, ioctl_val_type value) + { + int rc; + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + rc = ioctl(fd, opt, value); + set_fs(oldfs); + if (rc < 0) { + errno = -rc; + return rc; + } else + return rc; + } +# define getsockopt_val_ptr char* + +# define epoll_data data +# define epoll_data_type __u32 +#endif + +#define THIS_FILE "ioq_epoll" + +//#define TRACE_(expr) PJ_LOG(3,expr) +#define TRACE_(expr) + +/* + * Include common ioqueue abstraction. + */ +#include "ioqueue_common_abs.h" + +/* + * This describes each key. + */ +struct pj_ioqueue_key_t +{ + DECLARE_COMMON_KEY +}; + +struct queue +{ + pj_ioqueue_key_t *key; + enum ioqueue_event_type event_type; +}; + +/* + * This describes the I/O queue. + */ +struct pj_ioqueue_t +{ + DECLARE_COMMON_IOQUEUE + + unsigned max, count; + //pj_ioqueue_key_t hlist; + pj_ioqueue_key_t active_list; + int epfd; + //struct epoll_event *events; + //struct queue *queue; + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + pj_mutex_t *ref_cnt_mutex; + pj_ioqueue_key_t closing_list; + pj_ioqueue_key_t free_list; +#endif +}; + +/* Include implementation for common abstraction after we declare + * pj_ioqueue_key_t and pj_ioqueue_t. + */ +#include "ioqueue_common_abs.c" + +#if PJ_IOQUEUE_HAS_SAFE_UNREG +/* Scan closing keys to be put to free list again */ +static void scan_closing_keys(pj_ioqueue_t *ioqueue); +#endif + +/* + * pj_ioqueue_name() + */ +PJ_DEF(const char*) pj_ioqueue_name(void) +{ +#if defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL!=0 + return "epoll-kernel"; +#else + return "epoll"; +#endif +} + +/* + * pj_ioqueue_create() + * + * Create select ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + pj_ioqueue_t **p_ioqueue) +{ + pj_ioqueue_t *ioqueue; + pj_status_t rc; + pj_lock_t *lock; + int i; + + /* Check that arguments are valid. */ + PJ_ASSERT_RETURN(pool != NULL && p_ioqueue != NULL && + max_fd > 0, PJ_EINVAL); + + /* Check that size of pj_ioqueue_op_key_t is sufficient */ + PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >= + sizeof(union operation_key), PJ_EBUG); + + ioqueue = pj_pool_alloc(pool, sizeof(pj_ioqueue_t)); + + ioqueue_init(ioqueue); + + ioqueue->max = max_fd; + ioqueue->count = 0; + pj_list_init(&ioqueue->active_list); + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* When safe unregistration is used (the default), we pre-create + * all keys and put them in the free list. + */ + + /* Mutex to protect key's reference counter + * We don't want to use key's mutex or ioqueue's mutex because + * that would create deadlock situation in some cases. + */ + rc = pj_mutex_create_simple(pool, NULL, &ioqueue->ref_cnt_mutex); + if (rc != PJ_SUCCESS) + return rc; + + + /* Init key list */ + pj_list_init(&ioqueue->free_list); + pj_list_init(&ioqueue->closing_list); + + + /* Pre-create all keys according to max_fd */ + for ( i=0; iref_count = 0; + rc = pj_mutex_create_recursive(pool, NULL, &key->mutex); + if (rc != PJ_SUCCESS) { + key = ioqueue->free_list.next; + while (key != &ioqueue->free_list) { + pj_mutex_destroy(key->mutex); + key = key->next; + } + pj_mutex_destroy(ioqueue->ref_cnt_mutex); + return rc; + } + + pj_list_push_back(&ioqueue->free_list, key); + } +#endif + + rc = pj_lock_create_simple_mutex(pool, "ioq%p", &lock); + if (rc != PJ_SUCCESS) + return rc; + + rc = pj_ioqueue_set_lock(ioqueue, lock, PJ_TRUE); + if (rc != PJ_SUCCESS) + return rc; + + ioqueue->epfd = os_epoll_create(max_fd); + if (ioqueue->epfd < 0) { + ioqueue_destroy(ioqueue); + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); + } + + /*ioqueue->events = pj_pool_calloc(pool, max_fd, sizeof(struct epoll_event)); + PJ_ASSERT_RETURN(ioqueue->events != NULL, PJ_ENOMEM); + + ioqueue->queue = pj_pool_calloc(pool, max_fd, sizeof(struct queue)); + PJ_ASSERT_RETURN(ioqueue->queue != NULL, PJ_ENOMEM); + */ + PJ_LOG(4, ("pjlib", "epoll I/O Queue created (%p)", ioqueue)); + + *p_ioqueue = ioqueue; + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_destroy() + * + * Destroy ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue) +{ + pj_ioqueue_key_t *key; + + PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL); + PJ_ASSERT_RETURN(ioqueue->epfd > 0, PJ_EINVALIDOP); + + pj_lock_acquire(ioqueue->lock); + os_close(ioqueue->epfd); + ioqueue->epfd = 0; + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Destroy reference counters */ + key = ioqueue->active_list.next; + while (key != &ioqueue->active_list) { + pj_mutex_destroy(key->mutex); + key = key->next; + } + + key = ioqueue->closing_list.next; + while (key != &ioqueue->closing_list) { + pj_mutex_destroy(key->mutex); + key = key->next; + } + + key = ioqueue->free_list.next; + while (key != &ioqueue->free_list) { + pj_mutex_destroy(key->mutex); + key = key->next; + } + + pj_mutex_destroy(ioqueue->ref_cnt_mutex); +#endif + return ioqueue_destroy(ioqueue); +} + +/* + * pj_ioqueue_register_sock() + * + * Register a socket to ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioqueue, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **p_key) +{ + pj_ioqueue_key_t *key = NULL; + pj_uint32_t value; + struct epoll_event ev; + int status; + pj_status_t rc = PJ_SUCCESS; + + PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET && + cb && p_key, PJ_EINVAL); + + pj_lock_acquire(ioqueue->lock); + + if (ioqueue->count >= ioqueue->max) { + rc = PJ_ETOOMANY; + TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: too many files")); + goto on_return; + } + + /* Set socket to nonblocking. */ + value = 1; + if ((rc=os_ioctl(sock, FIONBIO, (ioctl_val_type)&value))) { + TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: ioctl rc=%d", + rc)); + rc = pj_get_netos_error(); + goto on_return; + } + + /* If safe unregistration (PJ_IOQUEUE_HAS_SAFE_UNREG) is used, get + * the key from the free list. Otherwise allocate a new one. + */ +#if PJ_IOQUEUE_HAS_SAFE_UNREG + + /* Scan closing_keys first to let them come back to free_list */ + scan_closing_keys(ioqueue); + + pj_assert(!pj_list_empty(&ioqueue->free_list)); + if (pj_list_empty(&ioqueue->free_list)) { + rc = PJ_ETOOMANY; + goto on_return; + } + + key = ioqueue->free_list.next; + pj_list_erase(key); +#else + /* Create key. */ + key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); +#endif + + rc = ioqueue_init_key(pool, ioqueue, key, sock, user_data, cb); + if (rc != PJ_SUCCESS) { + key = NULL; + goto on_return; + } + + /* Create key's mutex */ + /* rc = pj_mutex_create_recursive(pool, NULL, &key->mutex); + if (rc != PJ_SUCCESS) { + key = NULL; + goto on_return; + } +*/ + /* os_epoll_ctl. */ + ev.events = EPOLLIN | EPOLLERR; + ev.epoll_data = (epoll_data_type)key; + status = os_epoll_ctl(ioqueue->epfd, EPOLL_CTL_ADD, sock, &ev); + if (status < 0) { + rc = pj_get_os_error(); + pj_mutex_destroy(key->mutex); + key = NULL; + TRACE_((THIS_FILE, + "pj_ioqueue_register_sock error: os_epoll_ctl rc=%d", + status)); + goto on_return; + } + + /* Register */ + pj_list_insert_before(&ioqueue->active_list, key); + ++ioqueue->count; + + //TRACE_((THIS_FILE, "socket registered, count=%d", ioqueue->count)); + +on_return: + *p_key = key; + pj_lock_release(ioqueue->lock); + + return rc; +} + +#if PJ_IOQUEUE_HAS_SAFE_UNREG +/* Increment key's reference counter */ +static void increment_counter(pj_ioqueue_key_t *key) +{ + pj_mutex_lock(key->ioqueue->ref_cnt_mutex); + ++key->ref_count; + pj_mutex_unlock(key->ioqueue->ref_cnt_mutex); +} + +/* Decrement the key's reference counter, and when the counter reach zero, + * destroy the key. + * + * Note: MUST NOT CALL THIS FUNCTION WHILE HOLDING ioqueue's LOCK. + */ +static void decrement_counter(pj_ioqueue_key_t *key) +{ + pj_lock_acquire(key->ioqueue->lock); + pj_mutex_lock(key->ioqueue->ref_cnt_mutex); + --key->ref_count; + if (key->ref_count == 0) { + + pj_assert(key->closing == 1); + pj_gettickcount(&key->free_time); + key->free_time.msec += PJ_IOQUEUE_KEY_FREE_DELAY; + pj_time_val_normalize(&key->free_time); + + pj_list_erase(key); + pj_list_push_back(&key->ioqueue->closing_list, key); + + } + pj_mutex_unlock(key->ioqueue->ref_cnt_mutex); + pj_lock_release(key->ioqueue->lock); +} +#endif + +/* + * pj_ioqueue_unregister() + * + * Unregister handle from ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key) +{ + pj_ioqueue_t *ioqueue; + struct epoll_event ev; + int status; + + PJ_ASSERT_RETURN(key != NULL, PJ_EINVAL); + + ioqueue = key->ioqueue; + + /* Lock the key to make sure no callback is simultaneously modifying + * the key. We need to lock the key before ioqueue here to prevent + * deadlock. + */ + pj_mutex_lock(key->mutex); + + /* Also lock ioqueue */ + pj_lock_acquire(ioqueue->lock); + + pj_assert(ioqueue->count > 0); + --ioqueue->count; +#if !PJ_IOQUEUE_HAS_SAFE_UNREG + pj_list_erase(key); +#endif + + ev.events = 0; + ev.epoll_data = (epoll_data_type)key; + status = os_epoll_ctl( ioqueue->epfd, EPOLL_CTL_DEL, key->fd, &ev); + if (status != 0) { + pj_status_t rc = pj_get_os_error(); + pj_lock_release(ioqueue->lock); + return rc; + } + + /* Destroy the key. */ + pj_sock_close(key->fd); + + pj_lock_release(ioqueue->lock); + + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Mark key is closing. */ + key->closing = 1; + + /* Decrement counter. */ + decrement_counter(key); + + /* Done. */ + pj_mutex_unlock(key->mutex); +#else + pj_mutex_destroy(key->mutex); +#endif + + return PJ_SUCCESS; +} + +/* ioqueue_remove_from_set() + * This function is called from ioqueue_dispatch_event() to instruct + * the ioqueue to remove the specified descriptor from ioqueue's descriptor + * set for the specified event. + */ +static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + enum ioqueue_event_type event_type) +{ + if (event_type == WRITEABLE_EVENT) { + struct epoll_event ev; + + ev.events = EPOLLIN | EPOLLERR; + ev.epoll_data = (epoll_data_type)key; + os_epoll_ctl( ioqueue->epfd, EPOLL_CTL_MOD, key->fd, &ev); + } +} + +/* + * ioqueue_add_to_set() + * This function is called from pj_ioqueue_recv(), pj_ioqueue_send() etc + * to instruct the ioqueue to add the specified handle to ioqueue's descriptor + * set for the specified event. + */ +static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + enum ioqueue_event_type event_type ) +{ + if (event_type == WRITEABLE_EVENT) { + struct epoll_event ev; + + ev.events = EPOLLIN | EPOLLOUT | EPOLLERR; + ev.epoll_data = (epoll_data_type)key; + os_epoll_ctl( ioqueue->epfd, EPOLL_CTL_MOD, key->fd, &ev); + } +} + +#if PJ_IOQUEUE_HAS_SAFE_UNREG +/* Scan closing keys to be put to free list again */ +static void scan_closing_keys(pj_ioqueue_t *ioqueue) +{ + pj_time_val now; + pj_ioqueue_key_t *h; + + pj_gettickcount(&now); + h = ioqueue->closing_list.next; + while (h != &ioqueue->closing_list) { + pj_ioqueue_key_t *next = h->next; + + pj_assert(h->closing != 0); + + if (PJ_TIME_VAL_GTE(now, h->free_time)) { + pj_list_erase(h); + pj_list_push_back(&ioqueue->free_list, h); + } + h = next; + } +} +#endif + +/* + * pj_ioqueue_poll() + * + */ +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout) +{ + int i, count, processed; + int msec; + //struct epoll_event *events = ioqueue->events; + //struct queue *queue = ioqueue->queue; + struct epoll_event events[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL]; + struct queue queue[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL]; + pj_timestamp t1, t2; + + PJ_CHECK_STACK(); + + msec = timeout ? PJ_TIME_VAL_MSEC(*timeout) : 9000; + + TRACE_((THIS_FILE, "start os_epoll_wait, msec=%d", msec)); + pj_get_timestamp(&t1); + + //count = os_epoll_wait( ioqueue->epfd, events, ioqueue->max, msec); + count = os_epoll_wait( ioqueue->epfd, events, PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL, msec); + if (count == 0) { +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Check the closing keys only when there's no activity and when there are + * pending closing keys. + */ + if (count == 0 && !pj_list_empty(&ioqueue->closing_list)) { + pj_lock_acquire(ioqueue->lock); + scan_closing_keys(ioqueue); + pj_lock_release(ioqueue->lock); + } +#endif + TRACE_((THIS_FILE, "os_epoll_wait timed out")); + return count; + } + else if (count < 0) { + TRACE_((THIS_FILE, "os_epoll_wait error")); + return -pj_get_netos_error(); + } + + pj_get_timestamp(&t2); + TRACE_((THIS_FILE, "os_epoll_wait returns %d, time=%d usec", + count, pj_elapsed_usec(&t1, &t2))); + + /* Lock ioqueue. */ + pj_lock_acquire(ioqueue->lock); + + for (processed=0, i=0; iconnecting) && !IS_CLOSING(h)) { + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + increment_counter(h); +#endif + queue[processed].key = h; + queue[processed].event_type = WRITEABLE_EVENT; + ++processed; + } +#endif /* PJ_HAS_TCP */ + + /* + * Check for error condition. + */ + if (events[i].events & EPOLLERR && (h->connecting) && !IS_CLOSING(h)) { + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + increment_counter(h); +#endif + queue[processed].key = h; + queue[processed].event_type = EXCEPTION_EVENT; + ++processed; + } + } + pj_lock_release(ioqueue->lock); + + /* Now process the events. */ + for (i=0; i 0 but no descriptors are actually set! + */ + if (count > 0 && !processed && msec > 0) { + pj_thread_sleep(msec); + } + + pj_get_timestamp(&t1); + TRACE_((THIS_FILE, "ioqueue_poll() returns %d, time=%d usec", + processed, pj_elapsed_usec(&t2, &t1))); + + return processed; +} + diff --git a/pjlib/src/pj/ioqueue_linux_kernel.c b/pjlib/src/pj/ioqueue_linux_kernel.c new file mode 100644 index 0000000..706da06 --- /dev/null +++ b/pjlib/src/pj/ioqueue_linux_kernel.c @@ -0,0 +1,162 @@ +/* $Id: ioqueue_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#define THIS_FILE "ioqueue" + +#define PJ_IOQUEUE_IS_READ_OP(op) \ + ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM)) +#define PJ_IOQUEUE_IS_WRITE_OP(op) \ + ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO)) + + +#if PJ_HAS_TCP +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT) +# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT) +#else +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0 +# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0 +#endif + +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 +# define VALIDATE_FD_SET 1 +#else +# define VALIDATE_FD_SET 0 +#endif + +struct pj_ioqueue_key_t +{ + PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t) + pj_sock_t fd; + pj_ioqueue_operation_e op; + void *user_data; + pj_ioqueue_callback cb; +}; + +struct pj_ioqueue_t +{ +}; + +PJ_DEF(pj_ioqueue_t*) pj_ioqueue_create(pj_pool_t *pool, pj_size_t max_fd) +{ + return NULL; +} + +PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque) +{ + return 0; +} + +PJ_DEF(pj_ioqueue_key_t*) pj_ioqueue_register( pj_pool_t *pool, + pj_ioqueue_t *ioque, + pj_oshandle_t sock, + void *user_data, + const pj_ioqueue_callback *cb) +{ + return NULL; +} + +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key) +{ + return -1; +} + +PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) +{ + return NULL; +} + + +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) +{ + return -1; +} + +PJ_DEF(int) pj_ioqueue_read( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen) +{ + return -1; +} + +PJ_DEF(int) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + pj_sockaddr_t *addr, + int *addrlen) +{ + return -1; +} + +PJ_DEF(int) pj_ioqueue_write( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen) +{ + return -1; +} + +PJ_DEF(int) pj_ioqueue_sendto( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + const pj_sockaddr_t *addr, + int addrlen) +{ + return -1; +} + +#if PJ_HAS_TCP +/* + * Initiate overlapped accept() operation. + */ +PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen) +{ + return -1; +} + +/* + * Initiate overlapped connect() operation (well, it's non-blocking actually, + * since there's no overlapped version of connect()). + */ +PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ) +{ + return -1; +} +#endif /* PJ_HAS_TCP */ + diff --git a/pjlib/src/pj/ioqueue_select.c b/pjlib/src/pj/ioqueue_select.c new file mode 100644 index 0000000..74b87d8 --- /dev/null +++ b/pjlib/src/pj/ioqueue_select.c @@ -0,0 +1,926 @@ +/* $Id: ioqueue_select.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + */ + +/* + * sock_select.c + * + * This is the implementation of IOQueue using pj_sock_select(). + * It runs anywhere where pj_sock_select() is available (currently + * Win32, Linux, Linux kernel, etc.). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Now that we have access to OS'es , lets check again that + * PJ_IOQUEUE_MAX_HANDLES is not greater than FD_SETSIZE + */ +#if PJ_IOQUEUE_MAX_HANDLES > FD_SETSIZE +# error "PJ_IOQUEUE_MAX_HANDLES cannot be greater than FD_SETSIZE" +#endif + + +/* + * Include declaration from common abstraction. + */ +#include "ioqueue_common_abs.h" + +/* + * ISSUES with ioqueue_select() + * + * EAGAIN/EWOULDBLOCK error in recv(): + * - when multiple threads are working with the ioqueue, application + * may receive EAGAIN or EWOULDBLOCK in the receive callback. + * This error happens because more than one thread is watching for + * the same descriptor set, so when all of them call recv() or recvfrom() + * simultaneously, only one will succeed and the rest will get the error. + * + */ +#define THIS_FILE "ioq_select" + +/* + * The select ioqueue relies on socket functions (pj_sock_xxx()) to return + * the correct error code. + */ +#if PJ_RETURN_OS_ERROR(100) != PJ_STATUS_FROM_OS(100) +# error "Error reporting must be enabled for this function to work!" +#endif + +/* + * During debugging build, VALIDATE_FD_SET is set. + * This will check the validity of the fd_sets. + */ +/* +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 +# define VALIDATE_FD_SET 1 +#else +# define VALIDATE_FD_SET 0 +#endif +*/ +#define VALIDATE_FD_SET 0 + +#if 0 +# define TRACE__(args) PJ_LOG(3,args) +#else +# define TRACE__(args) +#endif + +/* + * This describes each key. + */ +struct pj_ioqueue_key_t +{ + DECLARE_COMMON_KEY +}; + +/* + * This describes the I/O queue itself. + */ +struct pj_ioqueue_t +{ + DECLARE_COMMON_IOQUEUE + + unsigned max, count; /* Max and current key count */ + int nfds; /* The largest fd value (for select)*/ + pj_ioqueue_key_t active_list; /* List of active keys. */ + pj_fd_set_t rfdset; + pj_fd_set_t wfdset; +#if PJ_HAS_TCP + pj_fd_set_t xfdset; +#endif + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + pj_mutex_t *ref_cnt_mutex; + pj_ioqueue_key_t closing_list; + pj_ioqueue_key_t free_list; +#endif +}; + +/* Proto */ +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 +static pj_status_t replace_udp_sock(pj_ioqueue_key_t *h); +#endif + +/* Include implementation for common abstraction after we declare + * pj_ioqueue_key_t and pj_ioqueue_t. + */ +#include "ioqueue_common_abs.c" + +#if PJ_IOQUEUE_HAS_SAFE_UNREG +/* Scan closing keys to be put to free list again */ +static void scan_closing_keys(pj_ioqueue_t *ioqueue); +#endif + +/* + * pj_ioqueue_name() + */ +PJ_DEF(const char*) pj_ioqueue_name(void) +{ + return "select"; +} + +/* + * Scan the socket descriptor sets for the largest descriptor. + * This value is needed by select(). + */ +#if defined(PJ_SELECT_NEEDS_NFDS) && PJ_SELECT_NEEDS_NFDS!=0 +static void rescan_fdset(pj_ioqueue_t *ioqueue) +{ + pj_ioqueue_key_t *key = ioqueue->active_list.next; + int max = 0; + + while (key != &ioqueue->active_list) { + if (key->fd > max) + max = key->fd; + key = key->next; + } + + ioqueue->nfds = max; +} +#else +static void rescan_fdset(pj_ioqueue_t *ioqueue) +{ + ioqueue->nfds = FD_SETSIZE-1; +} +#endif + + +/* + * pj_ioqueue_create() + * + * Create select ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + pj_ioqueue_t **p_ioqueue) +{ + pj_ioqueue_t *ioqueue; + pj_lock_t *lock; + unsigned i; + pj_status_t rc; + + /* Check that arguments are valid. */ + PJ_ASSERT_RETURN(pool != NULL && p_ioqueue != NULL && + max_fd > 0 && max_fd <= PJ_IOQUEUE_MAX_HANDLES, + PJ_EINVAL); + + /* Check that size of pj_ioqueue_op_key_t is sufficient */ + PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >= + sizeof(union operation_key), PJ_EBUG); + + /* Create and init common ioqueue stuffs */ + ioqueue = PJ_POOL_ALLOC_T(pool, pj_ioqueue_t); + ioqueue_init(ioqueue); + + ioqueue->max = max_fd; + ioqueue->count = 0; + PJ_FD_ZERO(&ioqueue->rfdset); + PJ_FD_ZERO(&ioqueue->wfdset); +#if PJ_HAS_TCP + PJ_FD_ZERO(&ioqueue->xfdset); +#endif + pj_list_init(&ioqueue->active_list); + + rescan_fdset(ioqueue); + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* When safe unregistration is used (the default), we pre-create + * all keys and put them in the free list. + */ + + /* Mutex to protect key's reference counter + * We don't want to use key's mutex or ioqueue's mutex because + * that would create deadlock situation in some cases. + */ + rc = pj_mutex_create_simple(pool, NULL, &ioqueue->ref_cnt_mutex); + if (rc != PJ_SUCCESS) + return rc; + + + /* Init key list */ + pj_list_init(&ioqueue->free_list); + pj_list_init(&ioqueue->closing_list); + + + /* Pre-create all keys according to max_fd */ + for (i=0; iref_count = 0; + rc = pj_mutex_create_recursive(pool, NULL, &key->mutex); + if (rc != PJ_SUCCESS) { + key = ioqueue->free_list.next; + while (key != &ioqueue->free_list) { + pj_mutex_destroy(key->mutex); + key = key->next; + } + pj_mutex_destroy(ioqueue->ref_cnt_mutex); + return rc; + } + + pj_list_push_back(&ioqueue->free_list, key); + } +#endif + + /* Create and init ioqueue mutex */ + rc = pj_lock_create_simple_mutex(pool, "ioq%p", &lock); + if (rc != PJ_SUCCESS) + return rc; + + rc = pj_ioqueue_set_lock(ioqueue, lock, PJ_TRUE); + if (rc != PJ_SUCCESS) + return rc; + + PJ_LOG(4, ("pjlib", "select() I/O Queue created (%p)", ioqueue)); + + *p_ioqueue = ioqueue; + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_destroy() + * + * Destroy ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue) +{ + pj_ioqueue_key_t *key; + + PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL); + + pj_lock_acquire(ioqueue->lock); + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Destroy reference counters */ + key = ioqueue->active_list.next; + while (key != &ioqueue->active_list) { + pj_mutex_destroy(key->mutex); + key = key->next; + } + + key = ioqueue->closing_list.next; + while (key != &ioqueue->closing_list) { + pj_mutex_destroy(key->mutex); + key = key->next; + } + + key = ioqueue->free_list.next; + while (key != &ioqueue->free_list) { + pj_mutex_destroy(key->mutex); + key = key->next; + } + + pj_mutex_destroy(ioqueue->ref_cnt_mutex); +#endif + + return ioqueue_destroy(ioqueue); +} + + +/* + * pj_ioqueue_register_sock() + * + * Register socket handle to ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioqueue, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **p_key) +{ + pj_ioqueue_key_t *key = NULL; +#if defined(PJ_WIN32) && PJ_WIN32!=0 || \ + defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 + u_long value; +#else + pj_uint32_t value; +#endif + pj_status_t rc = PJ_SUCCESS; + + PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET && + cb && p_key, PJ_EINVAL); + + pj_lock_acquire(ioqueue->lock); + + if (ioqueue->count >= ioqueue->max) { + rc = PJ_ETOOMANY; + goto on_return; + } + + /* If safe unregistration (PJ_IOQUEUE_HAS_SAFE_UNREG) is used, get + * the key from the free list. Otherwise allocate a new one. + */ +#if PJ_IOQUEUE_HAS_SAFE_UNREG + + /* Scan closing_keys first to let them come back to free_list */ + scan_closing_keys(ioqueue); + + pj_assert(!pj_list_empty(&ioqueue->free_list)); + if (pj_list_empty(&ioqueue->free_list)) { + rc = PJ_ETOOMANY; + goto on_return; + } + + key = ioqueue->free_list.next; + pj_list_erase(key); +#else + key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); +#endif + + rc = ioqueue_init_key(pool, ioqueue, key, sock, user_data, cb); + if (rc != PJ_SUCCESS) { + key = NULL; + goto on_return; + } + + /* Set socket to nonblocking. */ + value = 1; +#if defined(PJ_WIN32) && PJ_WIN32!=0 || \ + defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 + if (ioctlsocket(sock, FIONBIO, &value)) { +#else + if (ioctl(sock, FIONBIO, &value)) { +#endif + rc = pj_get_netos_error(); + goto on_return; + } + + + /* Put in active list. */ + pj_list_insert_before(&ioqueue->active_list, key); + ++ioqueue->count; + + /* Rescan fdset to get max descriptor */ + rescan_fdset(ioqueue); + +on_return: + /* On error, socket may be left in non-blocking mode. */ + *p_key = key; + pj_lock_release(ioqueue->lock); + + return rc; +} + +#if PJ_IOQUEUE_HAS_SAFE_UNREG +/* Increment key's reference counter */ +static void increment_counter(pj_ioqueue_key_t *key) +{ + pj_mutex_lock(key->ioqueue->ref_cnt_mutex); + ++key->ref_count; + pj_mutex_unlock(key->ioqueue->ref_cnt_mutex); +} + +/* Decrement the key's reference counter, and when the counter reach zero, + * destroy the key. + * + * Note: MUST NOT CALL THIS FUNCTION WHILE HOLDING ioqueue's LOCK. + */ +static void decrement_counter(pj_ioqueue_key_t *key) +{ + pj_lock_acquire(key->ioqueue->lock); + pj_mutex_lock(key->ioqueue->ref_cnt_mutex); + --key->ref_count; + if (key->ref_count == 0) { + + pj_assert(key->closing == 1); + pj_gettickcount(&key->free_time); + key->free_time.msec += PJ_IOQUEUE_KEY_FREE_DELAY; + pj_time_val_normalize(&key->free_time); + + pj_list_erase(key); + pj_list_push_back(&key->ioqueue->closing_list, key); + /* Rescan fdset to get max descriptor */ + rescan_fdset(key->ioqueue); + } + pj_mutex_unlock(key->ioqueue->ref_cnt_mutex); + pj_lock_release(key->ioqueue->lock); +} +#endif + + +/* + * pj_ioqueue_unregister() + * + * Unregister handle from ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key) +{ + pj_ioqueue_t *ioqueue; + + PJ_ASSERT_RETURN(key, PJ_EINVAL); + + ioqueue = key->ioqueue; + + /* Lock the key to make sure no callback is simultaneously modifying + * the key. We need to lock the key before ioqueue here to prevent + * deadlock. + */ + pj_mutex_lock(key->mutex); + + /* Also lock ioqueue */ + pj_lock_acquire(ioqueue->lock); + + pj_assert(ioqueue->count > 0); + --ioqueue->count; +#if !PJ_IOQUEUE_HAS_SAFE_UNREG + /* Ticket #520, key will be erased more than once */ + pj_list_erase(key); +#endif + PJ_FD_CLR(key->fd, &ioqueue->rfdset); + PJ_FD_CLR(key->fd, &ioqueue->wfdset); +#if PJ_HAS_TCP + PJ_FD_CLR(key->fd, &ioqueue->xfdset); +#endif + + /* Close socket. */ + pj_sock_close(key->fd); + + /* Clear callback */ + key->cb.on_accept_complete = NULL; + key->cb.on_connect_complete = NULL; + key->cb.on_read_complete = NULL; + key->cb.on_write_complete = NULL; + + /* Must release ioqueue lock first before decrementing counter, to + * prevent deadlock. + */ + pj_lock_release(ioqueue->lock); + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Mark key is closing. */ + key->closing = 1; + + /* Decrement counter. */ + decrement_counter(key); + + /* Done. */ + pj_mutex_unlock(key->mutex); +#else + pj_mutex_destroy(key->mutex); +#endif + + return PJ_SUCCESS; +} + + +/* This supposed to check whether the fd_set values are consistent + * with the operation currently set in each key. + */ +#if VALIDATE_FD_SET +static void validate_sets(const pj_ioqueue_t *ioqueue, + const pj_fd_set_t *rfdset, + const pj_fd_set_t *wfdset, + const pj_fd_set_t *xfdset) +{ + pj_ioqueue_key_t *key; + + /* + * This basicly would not work anymore. + * We need to lock key before performing the check, but we can't do + * so because we're holding ioqueue mutex. If we acquire key's mutex + * now, the will cause deadlock. + */ + pj_assert(0); + + key = ioqueue->active_list.next; + while (key != &ioqueue->active_list) { + if (!pj_list_empty(&key->read_list) +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 + || !pj_list_empty(&key->accept_list) +#endif + ) + { + pj_assert(PJ_FD_ISSET(key->fd, rfdset)); + } + else { + pj_assert(PJ_FD_ISSET(key->fd, rfdset) == 0); + } + if (!pj_list_empty(&key->write_list) +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 + || key->connecting +#endif + ) + { + pj_assert(PJ_FD_ISSET(key->fd, wfdset)); + } + else { + pj_assert(PJ_FD_ISSET(key->fd, wfdset) == 0); + } +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 + if (key->connecting) + { + pj_assert(PJ_FD_ISSET(key->fd, xfdset)); + } + else { + pj_assert(PJ_FD_ISSET(key->fd, xfdset) == 0); + } +#endif /* PJ_HAS_TCP */ + + key = key->next; + } +} +#endif /* VALIDATE_FD_SET */ + + +/* ioqueue_remove_from_set() + * This function is called from ioqueue_dispatch_event() to instruct + * the ioqueue to remove the specified descriptor from ioqueue's descriptor + * set for the specified event. + */ +static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + enum ioqueue_event_type event_type) +{ + pj_lock_acquire(ioqueue->lock); + + if (event_type == READABLE_EVENT) + PJ_FD_CLR((pj_sock_t)key->fd, &ioqueue->rfdset); + else if (event_type == WRITEABLE_EVENT) + PJ_FD_CLR((pj_sock_t)key->fd, &ioqueue->wfdset); +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0 + else if (event_type == EXCEPTION_EVENT) + PJ_FD_CLR((pj_sock_t)key->fd, &ioqueue->xfdset); +#endif + else + pj_assert(0); + + pj_lock_release(ioqueue->lock); +} + +/* + * ioqueue_add_to_set() + * This function is called from pj_ioqueue_recv(), pj_ioqueue_send() etc + * to instruct the ioqueue to add the specified handle to ioqueue's descriptor + * set for the specified event. + */ +static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + enum ioqueue_event_type event_type ) +{ + pj_lock_acquire(ioqueue->lock); + + if (event_type == READABLE_EVENT) + PJ_FD_SET((pj_sock_t)key->fd, &ioqueue->rfdset); + else if (event_type == WRITEABLE_EVENT) + PJ_FD_SET((pj_sock_t)key->fd, &ioqueue->wfdset); +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0 + else if (event_type == EXCEPTION_EVENT) + PJ_FD_SET((pj_sock_t)key->fd, &ioqueue->xfdset); +#endif + else + pj_assert(0); + + pj_lock_release(ioqueue->lock); +} + +#if PJ_IOQUEUE_HAS_SAFE_UNREG +/* Scan closing keys to be put to free list again */ +static void scan_closing_keys(pj_ioqueue_t *ioqueue) +{ + pj_time_val now; + pj_ioqueue_key_t *h; + + pj_gettickcount(&now); + h = ioqueue->closing_list.next; + while (h != &ioqueue->closing_list) { + pj_ioqueue_key_t *next = h->next; + + pj_assert(h->closing != 0); + + if (PJ_TIME_VAL_GTE(now, h->free_time)) { + pj_list_erase(h); + pj_list_push_back(&ioqueue->free_list, h); + } + h = next; + } +} +#endif + +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 +static pj_status_t replace_udp_sock(pj_ioqueue_key_t *h) +{ + enum flags { + HAS_PEER_ADDR = 1, + HAS_QOS = 2 + }; + pj_sock_t old_sock, new_sock = PJ_INVALID_SOCKET; + pj_sockaddr local_addr, rem_addr; + int val, addr_len; + pj_fd_set_t *fds[3]; + unsigned i, fds_cnt, flags=0; + pj_qos_params qos_params; + unsigned msec; + pj_status_t status; + + pj_lock_acquire(h->ioqueue->lock); + + old_sock = h->fd; + + /* Can only replace UDP socket */ + pj_assert(h->fd_type == pj_SOCK_DGRAM()); + + PJ_LOG(4,(THIS_FILE, "Attempting to replace UDP socket %d", old_sock)); + + /* Investigate the old socket */ + addr_len = sizeof(local_addr); + status = pj_sock_getsockname(old_sock, &local_addr, &addr_len); + if (status != PJ_SUCCESS) + goto on_error; + + addr_len = sizeof(rem_addr); + status = pj_sock_getpeername(old_sock, &rem_addr, &addr_len); + if (status == PJ_SUCCESS) + flags |= HAS_PEER_ADDR; + + status = pj_sock_get_qos_params(old_sock, &qos_params); + if (status == PJ_SUCCESS) + flags |= HAS_QOS; + + /* We're done with the old socket, close it otherwise we'll get + * error in bind() + */ + pj_sock_close(old_sock); + + /* Prepare the new socket */ + status = pj_sock_socket(local_addr.addr.sa_family, PJ_SOCK_DGRAM, 0, + &new_sock); + if (status != PJ_SUCCESS) + goto on_error; + + /* Even after the socket is closed, we'll still get "Address in use" + * errors, so force it with SO_REUSEADDR + */ + val = 1; + status = pj_sock_setsockopt(new_sock, SOL_SOCKET, SO_REUSEADDR, + &val, sizeof(val)); + if (status != PJ_SUCCESS) + goto on_error; + + /* The loop is silly, but what else can we do? */ + addr_len = pj_sockaddr_get_len(&local_addr); + for (msec=20; ; msec<1000? msec=msec*2 : 1000) { + status = pj_sock_bind(new_sock, &local_addr, addr_len); + if (status != PJ_STATUS_FROM_OS(EADDRINUSE)) + break; + PJ_LOG(4,(THIS_FILE, "Address is still in use, retrying..")); + pj_thread_sleep(msec); + } + + if (status != PJ_SUCCESS) + goto on_error; + + if (flags & HAS_QOS) { + status = pj_sock_set_qos_params(new_sock, &qos_params); + if (status != PJ_SUCCESS) + goto on_error; + } + + if (flags & HAS_PEER_ADDR) { + status = pj_sock_connect(new_sock, &rem_addr, addr_len); + if (status != PJ_SUCCESS) + goto on_error; + } + + /* Set socket to nonblocking. */ + val = 1; +#if defined(PJ_WIN32) && PJ_WIN32!=0 || \ + defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 + if (ioctlsocket(new_sock, FIONBIO, &val)) { +#else + if (ioctl(new_sock, FIONBIO, &val)) { +#endif + status = pj_get_netos_error(); + goto on_error; + } + + /* Replace the occurrence of old socket with new socket in the + * fd sets. + */ + fds_cnt = 0; + fds[fds_cnt++] = &h->ioqueue->rfdset; + fds[fds_cnt++] = &h->ioqueue->wfdset; +#if PJ_HAS_TCP + fds[fds_cnt++] = &h->ioqueue->xfdset; +#endif + + for (i=0; ifd = new_sock; + + PJ_LOG(4,(THIS_FILE, "UDP has been replaced successfully!")); + + pj_lock_release(h->ioqueue->lock); + + return PJ_SUCCESS; + +on_error: + if (new_sock != PJ_INVALID_SOCKET) + pj_sock_close(new_sock); + PJ_PERROR(1,(THIS_FILE, status, "Error replacing socket")); + pj_lock_release(h->ioqueue->lock); + return status; +} +#endif + + +/* + * pj_ioqueue_poll() + * + * Few things worth written: + * + * - we used to do only one callback called per poll, but it didn't go + * very well. The reason is because on some situation, the write + * callback gets called all the time, thus doesn't give the read + * callback to get called. This happens, for example, when user + * submit write operation inside the write callback. + * As the result, we changed the behaviour so that now multiple + * callbacks are called in a single poll. It should be fast too, + * just that we need to be carefull with the ioqueue data structs. + * + * - to guarantee preemptiveness etc, the poll function must strictly + * work on fd_set copy of the ioqueue (not the original one). + */ +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout) +{ + pj_fd_set_t rfdset, wfdset, xfdset; + int count, counter; + pj_ioqueue_key_t *h; + struct event + { + pj_ioqueue_key_t *key; + enum ioqueue_event_type event_type; + } event[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL]; + + PJ_ASSERT_RETURN(ioqueue, -PJ_EINVAL); + + /* Lock ioqueue before making fd_set copies */ + pj_lock_acquire(ioqueue->lock); + + /* We will only do select() when there are sockets to be polled. + * Otherwise select() will return error. + */ + if (PJ_FD_COUNT(&ioqueue->rfdset)==0 && + PJ_FD_COUNT(&ioqueue->wfdset)==0 +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0 + && PJ_FD_COUNT(&ioqueue->xfdset)==0 +#endif + ) + { +#if PJ_IOQUEUE_HAS_SAFE_UNREG + scan_closing_keys(ioqueue); +#endif + pj_lock_release(ioqueue->lock); + TRACE__((THIS_FILE, " poll: no fd is set")); + if (timeout) + pj_thread_sleep(PJ_TIME_VAL_MSEC(*timeout)); + return 0; + } + + /* Copy ioqueue's pj_fd_set_t to local variables. */ + pj_memcpy(&rfdset, &ioqueue->rfdset, sizeof(pj_fd_set_t)); + pj_memcpy(&wfdset, &ioqueue->wfdset, sizeof(pj_fd_set_t)); +#if PJ_HAS_TCP + pj_memcpy(&xfdset, &ioqueue->xfdset, sizeof(pj_fd_set_t)); +#else + PJ_FD_ZERO(&xfdset); +#endif + +#if VALIDATE_FD_SET + validate_sets(ioqueue, &rfdset, &wfdset, &xfdset); +#endif + + /* Unlock ioqueue before select(). */ + pj_lock_release(ioqueue->lock); + + count = pj_sock_select(ioqueue->nfds+1, &rfdset, &wfdset, &xfdset, + timeout); + + if (count == 0) + return 0; + else if (count < 0) + return -pj_get_netos_error(); + else if (count > PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL) + count = PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL; + + /* Scan descriptor sets for event and add the events in the event + * array to be processed later in this function. We do this so that + * events can be processed in parallel without holding ioqueue lock. + */ + pj_lock_acquire(ioqueue->lock); + + counter = 0; + + /* Scan for writable sockets first to handle piggy-back data + * coming with accept(). + */ + h = ioqueue->active_list.next; + for ( ; h!=&ioqueue->active_list && counternext) { + + if ( (key_has_pending_write(h) || key_has_pending_connect(h)) + && PJ_FD_ISSET(h->fd, &wfdset) && !IS_CLOSING(h)) + { +#if PJ_IOQUEUE_HAS_SAFE_UNREG + increment_counter(h); +#endif + event[counter].key = h; + event[counter].event_type = WRITEABLE_EVENT; + ++counter; + } + + /* Scan for readable socket. */ + if ((key_has_pending_read(h) || key_has_pending_accept(h)) + && PJ_FD_ISSET(h->fd, &rfdset) && !IS_CLOSING(h) && + counterfd, &xfdset) && + !IS_CLOSING(h) && counterlock); + + count = counter; + + /* Now process all events. The dispatch functions will take care + * of locking in each of the key + */ + for (counter=0; counter + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "os_symbian.h" + +class CIoqueueCallback; + +/* + * IO Queue structure. + */ +struct pj_ioqueue_t +{ + int eventCount; +}; + + +///////////////////////////////////////////////////////////////////////////// +// Class to encapsulate asynchronous socket operation. +// +class CIoqueueCallback : public CActive +{ +public: + static CIoqueueCallback* NewL(pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + pj_sock_t sock, + const pj_ioqueue_callback *cb, + void *user_data); + + // + // Start asynchronous recv() operation + // + pj_status_t StartRead(pj_ioqueue_op_key_t *op_key, + void *buf, pj_ssize_t *size, unsigned flags, + pj_sockaddr_t *addr, int *addrlen); + + // + // Start asynchronous accept() operation. + // + pj_status_t StartAccept(pj_ioqueue_op_key_t *op_key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen ); + + // + // Completion callback. + // + void RunL(); + + // + // CActive's DoCancel() + // + void DoCancel(); + + // + // Cancel operation and call callback. + // + void CancelOperation(pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_status); + + // + // Accessors + // + void* get_user_data() const + { + return user_data_; + } + void set_user_data(void *user_data) + { + user_data_ = user_data; + } + pj_ioqueue_op_key_t *get_op_key() const + { + return pending_data_.common_.op_key_; + } + CPjSocket* get_pj_socket() + { + return sock_; + } + +private: + // Type of pending operation. + enum Type { + TYPE_NONE, + TYPE_READ, + TYPE_ACCEPT, + }; + + // Static data. + pj_ioqueue_t *ioqueue_; + pj_ioqueue_key_t *key_; + CPjSocket *sock_; + pj_ioqueue_callback cb_; + void *user_data_; + + // Symbian data. + TPtr8 aBufferPtr_; + TInetAddr aAddress_; + + // Application data. + Type type_; + + union Pending_Data + { + struct Common + { + pj_ioqueue_op_key_t *op_key_; + } common_; + + struct Pending_Read + { + pj_ioqueue_op_key_t *op_key_; + pj_sockaddr_t *addr_; + int *addrlen_; + } read_; + + struct Pending_Accept + { + pj_ioqueue_op_key_t *op_key_; + pj_sock_t *new_sock_; + pj_sockaddr_t *local_; + pj_sockaddr_t *remote_; + int *addrlen_; + } accept_; + }; + + union Pending_Data pending_data_; + RSocket blank_sock_; + + CIoqueueCallback(pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, pj_sock_t sock, + const pj_ioqueue_callback *cb, void *user_data) + : CActive(CActive::EPriorityStandard), + ioqueue_(ioqueue), key_(key), sock_((CPjSocket*)sock), + user_data_(user_data), aBufferPtr_(NULL, 0), type_(TYPE_NONE) + { + pj_memcpy(&cb_, cb, sizeof(*cb)); + } + + + void ConstructL() + { + CActiveScheduler::Add(this); + } + + void HandleReadCompletion(); + CPjSocket *HandleAcceptCompletion(); +}; + + +CIoqueueCallback* CIoqueueCallback::NewL(pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + pj_sock_t sock, + const pj_ioqueue_callback *cb, + void *user_data) +{ + CIoqueueCallback *self = new CIoqueueCallback(ioqueue, key, sock, + cb, user_data); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + + return self; +} + + +// +// Start asynchronous recv() operation +// +pj_status_t CIoqueueCallback::StartRead(pj_ioqueue_op_key_t *op_key, + void *buf, pj_ssize_t *size, + unsigned flags, + pj_sockaddr_t *addr, int *addrlen) +{ + PJ_ASSERT_RETURN(IsActive()==false, PJ_EBUSY); + PJ_ASSERT_RETURN(pending_data_.common_.op_key_==NULL, PJ_EBUSY); + + flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC; + + pending_data_.read_.op_key_ = op_key; + pending_data_.read_.addr_ = addr; + pending_data_.read_.addrlen_ = addrlen; + + aBufferPtr_.Set((TUint8*)buf, 0, (TInt)*size); + + type_ = TYPE_READ; + if (addr && addrlen) { + sock_->Socket().RecvFrom(aBufferPtr_, aAddress_, flags, iStatus); + } else { + aAddress_.SetAddress(0); + aAddress_.SetPort(0); + + if (sock_->IsDatagram()) { + sock_->Socket().Recv(aBufferPtr_, flags, iStatus); + } else { + // Using static like this is not pretty, but we don't need to use + // the value anyway, hence doing it like this is probably most + // optimal. + static TSockXfrLength len; + sock_->Socket().RecvOneOrMore(aBufferPtr_, flags, iStatus, len); + } + } + + SetActive(); + return PJ_EPENDING; +} + + +// +// Start asynchronous accept() operation. +// +pj_status_t CIoqueueCallback::StartAccept(pj_ioqueue_op_key_t *op_key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen ) +{ + PJ_ASSERT_RETURN(IsActive()==false, PJ_EBUSY); + PJ_ASSERT_RETURN(pending_data_.common_.op_key_==NULL, PJ_EBUSY); + + // addrlen must be specified if local or remote is specified + PJ_ASSERT_RETURN((!local && !remote) || + (addrlen && *addrlen), PJ_EINVAL); + + pending_data_.accept_.op_key_ = op_key; + pending_data_.accept_.new_sock_ = new_sock; + pending_data_.accept_.local_ = local; + pending_data_.accept_.remote_ = remote; + pending_data_.accept_.addrlen_ = addrlen; + + // Create blank socket + blank_sock_.Open(PjSymbianOS::Instance()->SocketServ()); + + type_ = TYPE_ACCEPT; + sock_->Socket().Accept(blank_sock_, iStatus); + + SetActive(); + return PJ_EPENDING; +} + + +// +// Handle asynchronous RecvFrom() completion +// +void CIoqueueCallback::HandleReadCompletion() +{ + if (pending_data_.read_.addr_ && pending_data_.read_.addrlen_) { + PjSymbianOS::Addr2pj(aAddress_, + *(pj_sockaddr*)pending_data_.read_.addr_, + pending_data_.read_.addrlen_); + pending_data_.read_.addr_ = NULL; + pending_data_.read_.addrlen_ = NULL; + } + + pending_data_.read_.op_key_ = NULL; +} + + +// +// Handle asynchronous Accept() completion. +// +CPjSocket *CIoqueueCallback::HandleAcceptCompletion() +{ + CPjSocket *pjNewSock = new CPjSocket(get_pj_socket()->GetAf(), + get_pj_socket()->GetSockType(), + blank_sock_); + int addrlen = 0; + + if (pending_data_.accept_.new_sock_) { + *pending_data_.accept_.new_sock_ = (pj_sock_t)pjNewSock; + pending_data_.accept_.new_sock_ = NULL; + } + + if (pending_data_.accept_.local_) { + TInetAddr aAddr; + pj_sockaddr *ptr_sockaddr; + + blank_sock_.LocalName(aAddr); + ptr_sockaddr = (pj_sockaddr*)pending_data_.accept_.local_; + addrlen = *pending_data_.accept_.addrlen_; + PjSymbianOS::Addr2pj(aAddr, *ptr_sockaddr, &addrlen); + pending_data_.accept_.local_ = NULL; + } + + if (pending_data_.accept_.remote_) { + TInetAddr aAddr; + pj_sockaddr *ptr_sockaddr; + + blank_sock_.RemoteName(aAddr); + ptr_sockaddr = (pj_sockaddr*)pending_data_.accept_.remote_; + addrlen = *pending_data_.accept_.addrlen_; + PjSymbianOS::Addr2pj(aAddr, *ptr_sockaddr, &addrlen); + pending_data_.accept_.remote_ = NULL; + } + + if (pending_data_.accept_.addrlen_) { + if (addrlen == 0) { + if (pjNewSock->GetAf() == PJ_AF_INET) + addrlen = sizeof(pj_sockaddr_in); + else if (pjNewSock->GetAf() == PJ_AF_INET6) + addrlen = sizeof(pj_sockaddr_in6); + else { + pj_assert(!"Unsupported address family"); + } + } + *pending_data_.accept_.addrlen_ = addrlen; + pending_data_.accept_.addrlen_ = NULL; + } + + return pjNewSock; +} + + +// +// Completion callback. +// +void CIoqueueCallback::RunL() +{ + pj_ioqueue_t *ioq = ioqueue_; + Type cur_type = type_; + + type_ = TYPE_NONE; + + if (cur_type == TYPE_READ) { + // + // Completion of asynchronous RecvFrom() + // + + /* Clear op_key (save it to temp variable first!) */ + pj_ioqueue_op_key_t *op_key = pending_data_.read_.op_key_; + pending_data_.read_.op_key_ = NULL; + + // Handle failure condition + if (iStatus != KErrNone) { + if (cb_.on_read_complete) { + cb_.on_read_complete( key_, op_key, + -PJ_RETURN_OS_ERROR(iStatus.Int())); + } + return; + } + + HandleReadCompletion(); + + /* Call callback */ + if (cb_.on_read_complete) { + cb_.on_read_complete(key_, op_key, aBufferPtr_.Length()); + } + + } else if (cur_type == TYPE_ACCEPT) { + // + // Completion of asynchronous Accept() + // + + /* Clear op_key (save it to temp variable first!) */ + pj_ioqueue_op_key_t *op_key = pending_data_.read_.op_key_; + pending_data_.read_.op_key_ = NULL; + + // Handle failure condition + if (iStatus != KErrNone) { + if (pending_data_.accept_.new_sock_) + *pending_data_.accept_.new_sock_ = PJ_INVALID_SOCKET; + + if (cb_.on_accept_complete) { + cb_.on_accept_complete( key_, op_key, PJ_INVALID_SOCKET, + -PJ_RETURN_OS_ERROR(iStatus.Int())); + } + return; + } + + CPjSocket *pjNewSock = HandleAcceptCompletion(); + + // Call callback. + if (cb_.on_accept_complete) { + cb_.on_accept_complete( key_, op_key, (pj_sock_t)pjNewSock, + PJ_SUCCESS); + } + } + + ioq->eventCount++; +} + +// +// CActive's DoCancel() +// +void CIoqueueCallback::DoCancel() +{ + if (type_ == TYPE_READ) + sock_->Socket().CancelRecv(); + else if (type_ == TYPE_ACCEPT) + sock_->Socket().CancelAccept(); + + type_ = TYPE_NONE; + pending_data_.common_.op_key_ = NULL; +} + +// +// Cancel operation and call callback. +// +void CIoqueueCallback::CancelOperation(pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_status) +{ + Type cur_type = type_; + + pj_assert(op_key == pending_data_.common_.op_key_); + + Cancel(); + + if (cur_type == TYPE_READ) { + if (cb_.on_read_complete) + cb_.on_read_complete(key_, op_key, bytes_status); + } else if (cur_type == TYPE_ACCEPT) + ; +} + + +///////////////////////////////////////////////////////////////////////////// +/* + * IO Queue key structure. + */ +struct pj_ioqueue_key_t +{ + CIoqueueCallback *cbObj; +}; + + +/* + * Return the name of the ioqueue implementation. + */ +PJ_DEF(const char*) pj_ioqueue_name(void) +{ + return "ioqueue-symbian"; +} + + +/* + * Create a new I/O Queue framework. + */ +PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + pj_ioqueue_t **p_ioqueue) +{ + pj_ioqueue_t *ioq; + + PJ_UNUSED_ARG(max_fd); + + ioq = PJ_POOL_ZALLOC_T(pool, pj_ioqueue_t); + *p_ioqueue = ioq; + return PJ_SUCCESS; +} + + +/* + * Destroy the I/O queue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioq ) +{ + PJ_UNUSED_ARG(ioq); + return PJ_SUCCESS; +} + + +/* + * Set the lock object to be used by the I/O Queue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioq, + pj_lock_t *lock, + pj_bool_t auto_delete ) +{ + /* Don't really need lock for now */ + PJ_UNUSED_ARG(ioq); + + if (auto_delete) { + pj_lock_destroy(lock); + } + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_ioqueue_set_default_concurrency(pj_ioqueue_t *ioqueue, + pj_bool_t allow) +{ + /* Not supported, just return PJ_SUCCESS silently */ + PJ_UNUSED_ARG(ioqueue); + PJ_UNUSED_ARG(allow); + return PJ_SUCCESS; +} + +/* + * Register a socket to the I/O queue framework. + */ +PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioq, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **p_key ) +{ + pj_ioqueue_key_t *key; + + key = PJ_POOL_ZALLOC_T(pool, pj_ioqueue_key_t); + key->cbObj = CIoqueueCallback::NewL(ioq, key, sock, cb, user_data); + + *p_key = key; + return PJ_SUCCESS; +} + +/* + * Unregister from the I/O Queue framework. + */ +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key ) +{ + if (key == NULL || key->cbObj == NULL) + return PJ_SUCCESS; + + // Cancel pending async object + if (key->cbObj) { + key->cbObj->Cancel(); + } + + // Close socket. + key->cbObj->get_pj_socket()->Socket().Close(); + delete key->cbObj->get_pj_socket(); + + // Delete async object. + if (key->cbObj) { + delete key->cbObj; + key->cbObj = NULL; + } + + return PJ_SUCCESS; +} + + +/* + * Get user data associated with an ioqueue key. + */ +PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) +{ + return key->cbObj->get_user_data(); +} + + +/* + * Set or change the user data to be associated with the file descriptor or + * handle or socket descriptor. + */ +PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key, + void *user_data, + void **old_data) +{ + if (old_data) + *old_data = key->cbObj->get_user_data(); + key->cbObj->set_user_data(user_data); + + return PJ_SUCCESS; +} + + +/* + * Initialize operation key. + */ +PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key, + pj_size_t size ) +{ + pj_bzero(op_key, size); +} + + +/* + * Check if operation is pending on the specified operation key. + */ +PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key ) +{ + return key->cbObj->get_op_key()==op_key && + key->cbObj->IsActive(); +} + + +/* + * Post completion status to the specified operation key and call the + * appropriate callback. + */ +PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_status ) +{ + if (pj_ioqueue_is_pending(key, op_key)) { + key->cbObj->CancelOperation(op_key, bytes_status); + } + return PJ_SUCCESS; +} + + +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 +/** + * Instruct I/O Queue to accept incoming connection on the specified + * listening socket. + */ +PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen ) +{ + + return key->cbObj->StartAccept(op_key, new_sock, local, remote, addrlen); +} + + +/* + * Initiate non-blocking socket connect. + */ +PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ) +{ + pj_status_t status; + + RSocket &rSock = key->cbObj->get_pj_socket()->Socket(); + TInetAddr inetAddr; + TRequestStatus reqStatus; + + // Return failure if access point is marked as down by app. + PJ_SYMBIAN_CHECK_CONNECTION(); + + // Convert address + status = PjSymbianOS::pj2Addr(*(const pj_sockaddr*)addr, addrlen, + inetAddr); + if (status != PJ_SUCCESS) + return status; + + // We don't support async connect for now. + PJ_TODO(IOQUEUE_SUPPORT_ASYNC_CONNECT); + + rSock.Connect(inetAddr, reqStatus); + User::WaitForRequest(reqStatus); + + if (reqStatus == KErrNone) + return PJ_SUCCESS; + + return PJ_RETURN_OS_ERROR(reqStatus.Int()); +} + + +#endif /* PJ_HAS_TCP */ + +/* + * Poll the I/O Queue for completed events. + */ +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioq, + const pj_time_val *timeout) +{ + /* Polling is not necessary on Symbian, since all async activities + * are registered to active scheduler. + */ + PJ_UNUSED_ARG(ioq); + PJ_UNUSED_ARG(timeout); + return 0; +} + + +/* + * Instruct the I/O Queue to read from the specified handle. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + void *buffer, + pj_ssize_t *length, + pj_uint32_t flags ) +{ + // If socket has reader, delete it. + if (key->cbObj->get_pj_socket()->Reader()) + key->cbObj->get_pj_socket()->DestroyReader(); + + // Clear flag + flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC; + return key->cbObj->StartRead(op_key, buffer, length, flags, NULL, NULL); +} + + +/* + * This function behaves similarly as #pj_ioqueue_recv(), except that it is + * normally called for socket, and the remote address will also be returned + * along with the data. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + void *buffer, + pj_ssize_t *length, + pj_uint32_t flags, + pj_sockaddr_t *addr, + int *addrlen) +{ + CPjSocket *sock = key->cbObj->get_pj_socket(); + + // If address is specified, check that the length match the + // address family + if (addr || addrlen) { + PJ_ASSERT_RETURN(addr && addrlen && *addrlen, PJ_EINVAL); + if (sock->GetAf() == PJ_AF_INET) { + PJ_ASSERT_RETURN(*addrlen>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL); + } else if (sock->GetAf() == PJ_AF_INET6) { + PJ_ASSERT_RETURN(*addrlen>=(int)sizeof(pj_sockaddr_in6), PJ_EINVAL); + } + } + + // If socket has reader, delete it. + if (sock->Reader()) + sock->DestroyReader(); + + if (key->cbObj->IsActive()) + return PJ_EBUSY; + + // Clear flag + flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC; + return key->cbObj->StartRead(op_key, buffer, length, flags, addr, addrlen); +} + + +/* + * Instruct the I/O Queue to write to the handle. + */ +PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + const void *data, + pj_ssize_t *length, + pj_uint32_t flags ) +{ + TRequestStatus reqStatus; + TPtrC8 aBuffer((const TUint8*)data, (TInt)*length); + TSockXfrLength aLen; + + PJ_UNUSED_ARG(op_key); + + // Forcing pending operation is not supported. + PJ_ASSERT_RETURN((flags & PJ_IOQUEUE_ALWAYS_ASYNC)==0, PJ_EINVAL); + + // Return failure if access point is marked as down by app. + PJ_SYMBIAN_CHECK_CONNECTION(); + + // Clear flag + flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC; + + key->cbObj->get_pj_socket()->Socket().Send(aBuffer, flags, reqStatus, aLen); + User::WaitForRequest(reqStatus); + + if (reqStatus.Int() != KErrNone) + return PJ_RETURN_OS_ERROR(reqStatus.Int()); + + //At least in UIQ Emulator, aLen.Length() reports incorrect length + //for UDP (some newlc.com users seem to have reported this too). + //*length = aLen.Length(); + return PJ_SUCCESS; +} + + +/* + * Instruct the I/O Queue to write to the handle. + */ +PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + const void *data, + pj_ssize_t *length, + pj_uint32_t flags, + const pj_sockaddr_t *addr, + int addrlen) +{ + TRequestStatus reqStatus; + TPtrC8 aBuffer; + TInetAddr inetAddr; + TSockXfrLength aLen; + pj_status_t status; + + PJ_UNUSED_ARG(op_key); + + // Forcing pending operation is not supported. + PJ_ASSERT_RETURN((flags & PJ_IOQUEUE_ALWAYS_ASYNC)==0, PJ_EINVAL); + + // Return failure if access point is marked as down by app. + PJ_SYMBIAN_CHECK_CONNECTION(); + + // Convert address + status = PjSymbianOS::pj2Addr(*(const pj_sockaddr*)addr, addrlen, + inetAddr); + if (status != PJ_SUCCESS) + return status; + + // Clear flag + flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC; + + aBuffer.Set((const TUint8*)data, (TInt)*length); + CPjSocket *pjSock = key->cbObj->get_pj_socket(); + + pjSock->Socket().SendTo(aBuffer, inetAddr, flags, reqStatus, aLen); + User::WaitForRequest(reqStatus); + + if (reqStatus.Int() != KErrNone) + return PJ_RETURN_OS_ERROR(reqStatus.Int()); + + //At least in UIQ Emulator, aLen.Length() reports incorrect length + //for UDP (some newlc.com users seem to have reported this too). + //*length = aLen.Length(); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_ioqueue_set_concurrency(pj_ioqueue_key_t *key, + pj_bool_t allow) +{ + /* Not supported, just return PJ_SUCCESS silently */ + PJ_UNUSED_ARG(key); + PJ_UNUSED_ARG(allow); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_ioqueue_lock_key(pj_ioqueue_key_t *key) +{ + /* Not supported, just return PJ_SUCCESS silently */ + PJ_UNUSED_ARG(key); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_ioqueue_unlock_key(pj_ioqueue_key_t *key) +{ + /* Not supported, just return PJ_SUCCESS silently */ + PJ_UNUSED_ARG(key); + return PJ_SUCCESS; +} diff --git a/pjlib/src/pj/ioqueue_winnt.c b/pjlib/src/pj/ioqueue_winnt.c new file mode 100644 index 0000000..fbe8ab8 --- /dev/null +++ b/pjlib/src/pj/ioqueue_winnt.c @@ -0,0 +1,1443 @@ +/* $Id: ioqueue_winnt.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 +# include +#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 +# include +#endif + +#if defined(PJ_HAS_MSWSOCK_H) && PJ_HAS_MSWSOCK_H != 0 +# include +#endif + + +/* The address specified in AcceptEx() must be 16 more than the size of + * SOCKADDR (source: MSDN). + */ +#define ACCEPT_ADDR_LEN (sizeof(pj_sockaddr_in)+16) + +typedef struct generic_overlapped +{ + WSAOVERLAPPED overlapped; + pj_ioqueue_operation_e operation; +} generic_overlapped; + +/* + * OVERLAPPPED structure for send and receive. + */ +typedef struct ioqueue_overlapped +{ + WSAOVERLAPPED overlapped; + pj_ioqueue_operation_e operation; + WSABUF wsabuf; + pj_sockaddr_in dummy_addr; + int dummy_addrlen; +} ioqueue_overlapped; + +#if PJ_HAS_TCP +/* + * OVERLAP structure for accept. + */ +typedef struct ioqueue_accept_rec +{ + WSAOVERLAPPED overlapped; + pj_ioqueue_operation_e operation; + pj_sock_t newsock; + pj_sock_t *newsock_ptr; + int *addrlen; + void *remote; + void *local; + char accept_buf[2 * ACCEPT_ADDR_LEN]; +} ioqueue_accept_rec; +#endif + +/* + * Structure to hold pending operation key. + */ +union operation_key +{ + generic_overlapped generic; + ioqueue_overlapped overlapped; +#if PJ_HAS_TCP + ioqueue_accept_rec accept; +#endif +}; + +/* Type of handle in the key. */ +enum handle_type +{ + HND_IS_UNKNOWN, + HND_IS_FILE, + HND_IS_SOCKET, +}; + +enum { POST_QUIT_LEN = 0xFFFFDEADUL }; + +/* + * Structure for individual socket. + */ +struct pj_ioqueue_key_t +{ + PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t); + + pj_ioqueue_t *ioqueue; + HANDLE hnd; + void *user_data; + enum handle_type hnd_type; + pj_ioqueue_callback cb; + pj_bool_t allow_concurrent; + +#if PJ_HAS_TCP + int connecting; +#endif + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + pj_atomic_t *ref_count; + pj_bool_t closing; + pj_time_val free_time; + pj_mutex_t *mutex; +#endif + +}; + +/* + * IO Queue structure. + */ +struct pj_ioqueue_t +{ + HANDLE iocp; + pj_lock_t *lock; + pj_bool_t auto_delete_lock; + pj_bool_t default_concurrency; + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + pj_ioqueue_key_t active_list; + pj_ioqueue_key_t free_list; + pj_ioqueue_key_t closing_list; +#endif + + /* These are to keep track of connecting sockets */ +#if PJ_HAS_TCP + unsigned event_count; + HANDLE event_pool[MAXIMUM_WAIT_OBJECTS+1]; + unsigned connecting_count; + HANDLE connecting_handles[MAXIMUM_WAIT_OBJECTS+1]; + pj_ioqueue_key_t *connecting_keys[MAXIMUM_WAIT_OBJECTS+1]; +#endif +}; + + +#if PJ_IOQUEUE_HAS_SAFE_UNREG +/* Prototype */ +static void scan_closing_keys(pj_ioqueue_t *ioqueue); +#endif + + +#if PJ_HAS_TCP +/* + * Process the socket when the overlapped accept() completed. + */ +static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key, + ioqueue_accept_rec *accept_overlapped) +{ + struct sockaddr *local; + struct sockaddr *remote; + int locallen, remotelen; + pj_status_t status; + + PJ_CHECK_STACK(); + + /* On WinXP or later, use SO_UPDATE_ACCEPT_CONTEXT so that socket + * addresses can be obtained with getsockname() and getpeername(). + */ + status = setsockopt(accept_overlapped->newsock, SOL_SOCKET, + SO_UPDATE_ACCEPT_CONTEXT, + (char*)&key->hnd, + sizeof(SOCKET)); + /* SO_UPDATE_ACCEPT_CONTEXT is for WinXP or later. + * So ignore the error status. + */ + + /* Operation complete immediately. */ + if (accept_overlapped->addrlen) { + GetAcceptExSockaddrs( accept_overlapped->accept_buf, + 0, + ACCEPT_ADDR_LEN, + ACCEPT_ADDR_LEN, + &local, + &locallen, + &remote, + &remotelen); + if (*accept_overlapped->addrlen >= locallen) { + if (accept_overlapped->local) + pj_memcpy(accept_overlapped->local, local, locallen); + if (accept_overlapped->remote) + pj_memcpy(accept_overlapped->remote, remote, locallen); + } else { + if (accept_overlapped->local) + pj_bzero(accept_overlapped->local, + *accept_overlapped->addrlen); + if (accept_overlapped->remote) + pj_bzero(accept_overlapped->remote, + *accept_overlapped->addrlen); + } + + *accept_overlapped->addrlen = locallen; + } + if (accept_overlapped->newsock_ptr) + *accept_overlapped->newsock_ptr = accept_overlapped->newsock; + accept_overlapped->operation = 0; +} + +static void erase_connecting_socket( pj_ioqueue_t *ioqueue, unsigned pos) +{ + pj_ioqueue_key_t *key = ioqueue->connecting_keys[pos]; + HANDLE hEvent = ioqueue->connecting_handles[pos]; + + /* Remove key from array of connecting handles. */ + pj_array_erase(ioqueue->connecting_keys, sizeof(key), + ioqueue->connecting_count, pos); + pj_array_erase(ioqueue->connecting_handles, sizeof(HANDLE), + ioqueue->connecting_count, pos); + --ioqueue->connecting_count; + + /* Disassociate the socket from the event. */ + WSAEventSelect((pj_sock_t)key->hnd, hEvent, 0); + + /* Put event object to pool. */ + if (ioqueue->event_count < MAXIMUM_WAIT_OBJECTS) { + ioqueue->event_pool[ioqueue->event_count++] = hEvent; + } else { + /* Shouldn't happen. There should be no more pending connections + * than max. + */ + pj_assert(0); + CloseHandle(hEvent); + } + +} + +/* + * Poll for the completion of non-blocking connect(). + * If there's a completion, the function return the key of the completed + * socket, and 'result' argument contains the connect() result. If connect() + * succeeded, 'result' will have value zero, otherwise will have the error + * code. + */ +static int check_connecting( pj_ioqueue_t *ioqueue ) +{ + if (ioqueue->connecting_count) { + int i, count; + struct + { + pj_ioqueue_key_t *key; + pj_status_t status; + } events[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL-1]; + + pj_lock_acquire(ioqueue->lock); + for (count=0; countconnecting_count, + ioqueue->connecting_handles, + FALSE, 0); + if (result >= WAIT_OBJECT_0 && + result < WAIT_OBJECT_0+ioqueue->connecting_count) + { + WSANETWORKEVENTS net_events; + + /* Got completed connect(). */ + unsigned pos = result - WAIT_OBJECT_0; + events[count].key = ioqueue->connecting_keys[pos]; + + /* See whether connect has succeeded. */ + WSAEnumNetworkEvents((pj_sock_t)events[count].key->hnd, + ioqueue->connecting_handles[pos], + &net_events); + events[count].status = + PJ_STATUS_FROM_OS(net_events.iErrorCode[FD_CONNECT_BIT]); + + /* Erase socket from pending connect. */ + erase_connecting_socket(ioqueue, pos); + } else { + /* No more events */ + break; + } + } + pj_lock_release(ioqueue->lock); + + /* Call callbacks. */ + for (i=0; icb.on_connect_complete) { + events[i].key->cb.on_connect_complete(events[i].key, + events[i].status); + } + } + + return count; + } + + return 0; + +} +#endif + +/* + * pj_ioqueue_name() + */ +PJ_DEF(const char*) pj_ioqueue_name(void) +{ + return "iocp"; +} + +/* + * pj_ioqueue_create() + */ +PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + pj_ioqueue_t **p_ioqueue) +{ + pj_ioqueue_t *ioqueue; + unsigned i; + pj_status_t rc; + + PJ_UNUSED_ARG(max_fd); + PJ_ASSERT_RETURN(pool && p_ioqueue, PJ_EINVAL); + + rc = sizeof(union operation_key); + + /* Check that sizeof(pj_ioqueue_op_key_t) makes sense. */ + PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >= + sizeof(union operation_key), PJ_EBUG); + + /* Create IOCP */ + ioqueue = pj_pool_zalloc(pool, sizeof(*ioqueue)); + ioqueue->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if (ioqueue->iocp == NULL) + return PJ_RETURN_OS_ERROR(GetLastError()); + + /* Create IOCP mutex */ + rc = pj_lock_create_recursive_mutex(pool, NULL, &ioqueue->lock); + if (rc != PJ_SUCCESS) { + CloseHandle(ioqueue->iocp); + return rc; + } + + ioqueue->auto_delete_lock = PJ_TRUE; + ioqueue->default_concurrency = PJ_IOQUEUE_DEFAULT_ALLOW_CONCURRENCY; + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* + * Create and initialize key pools. + */ + pj_list_init(&ioqueue->active_list); + pj_list_init(&ioqueue->free_list); + pj_list_init(&ioqueue->closing_list); + + /* Preallocate keys according to max_fd setting, and put them + * in free_list. + */ + for (i=0; iref_count); + if (rc != PJ_SUCCESS) { + key = ioqueue->free_list.next; + while (key != &ioqueue->free_list) { + pj_atomic_destroy(key->ref_count); + pj_mutex_destroy(key->mutex); + key = key->next; + } + CloseHandle(ioqueue->iocp); + return rc; + } + + rc = pj_mutex_create_recursive(pool, "ioqkey", &key->mutex); + if (rc != PJ_SUCCESS) { + pj_atomic_destroy(key->ref_count); + key = ioqueue->free_list.next; + while (key != &ioqueue->free_list) { + pj_atomic_destroy(key->ref_count); + pj_mutex_destroy(key->mutex); + key = key->next; + } + CloseHandle(ioqueue->iocp); + return rc; + } + + pj_list_push_back(&ioqueue->free_list, key); + } +#endif + + *p_ioqueue = ioqueue; + + PJ_LOG(4, ("pjlib", "WinNT IOCP I/O Queue created (%p)", ioqueue)); + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_destroy() + */ +PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioqueue ) +{ +#if PJ_HAS_TCP + unsigned i; +#endif + pj_ioqueue_key_t *key; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL); + + pj_lock_acquire(ioqueue->lock); + +#if PJ_HAS_TCP + /* Destroy events in the pool */ + for (i=0; ievent_count; ++i) { + CloseHandle(ioqueue->event_pool[i]); + } + ioqueue->event_count = 0; +#endif + + if (CloseHandle(ioqueue->iocp) != TRUE) + return PJ_RETURN_OS_ERROR(GetLastError()); + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Destroy reference counters */ + key = ioqueue->active_list.next; + while (key != &ioqueue->active_list) { + pj_atomic_destroy(key->ref_count); + pj_mutex_destroy(key->mutex); + key = key->next; + } + + key = ioqueue->closing_list.next; + while (key != &ioqueue->closing_list) { + pj_atomic_destroy(key->ref_count); + pj_mutex_destroy(key->mutex); + key = key->next; + } + + key = ioqueue->free_list.next; + while (key != &ioqueue->free_list) { + pj_atomic_destroy(key->ref_count); + pj_mutex_destroy(key->mutex); + key = key->next; + } +#endif + + if (ioqueue->auto_delete_lock) + pj_lock_destroy(ioqueue->lock); + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_ioqueue_set_default_concurrency(pj_ioqueue_t *ioqueue, + pj_bool_t allow) +{ + PJ_ASSERT_RETURN(ioqueue != NULL, PJ_EINVAL); + ioqueue->default_concurrency = allow; + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_set_lock() + */ +PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioqueue, + pj_lock_t *lock, + pj_bool_t auto_delete ) +{ + PJ_ASSERT_RETURN(ioqueue && lock, PJ_EINVAL); + + if (ioqueue->auto_delete_lock) { + pj_lock_destroy(ioqueue->lock); + } + + ioqueue->lock = lock; + ioqueue->auto_delete_lock = auto_delete; + + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_register_sock() + */ +PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioqueue, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **key ) +{ + HANDLE hioq; + pj_ioqueue_key_t *rec; + u_long value; + int rc; + + PJ_ASSERT_RETURN(pool && ioqueue && cb && key, PJ_EINVAL); + + pj_lock_acquire(ioqueue->lock); + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Scan closing list first to release unused keys. + * Must do this with lock acquired. + */ + scan_closing_keys(ioqueue); + + /* If safe unregistration is used, then get the key record from + * the free list. + */ + if (pj_list_empty(&ioqueue->free_list)) { + pj_lock_release(ioqueue->lock); + return PJ_ETOOMANY; + } + + rec = ioqueue->free_list.next; + pj_list_erase(rec); + + /* Set initial reference count to 1 */ + pj_assert(pj_atomic_get(rec->ref_count) == 0); + pj_atomic_inc(rec->ref_count); + + rec->closing = 0; + +#else + rec = pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); +#endif + + /* Build the key for this socket. */ + rec->ioqueue = ioqueue; + rec->hnd = (HANDLE)sock; + rec->hnd_type = HND_IS_SOCKET; + rec->user_data = user_data; + pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback)); + + /* Set concurrency for this handle */ + rc = pj_ioqueue_set_concurrency(rec, ioqueue->default_concurrency); + if (rc != PJ_SUCCESS) { + pj_lock_release(ioqueue->lock); + return rc; + } + +#if PJ_HAS_TCP + rec->connecting = 0; +#endif + + /* Set socket to nonblocking. */ + value = 1; + rc = ioctlsocket(sock, FIONBIO, &value); + if (rc != 0) { + pj_lock_release(ioqueue->lock); + return PJ_RETURN_OS_ERROR(WSAGetLastError()); + } + + /* Associate with IOCP */ + hioq = CreateIoCompletionPort((HANDLE)sock, ioqueue->iocp, (DWORD)rec, 0); + if (!hioq) { + pj_lock_release(ioqueue->lock); + return PJ_RETURN_OS_ERROR(GetLastError()); + } + + *key = rec; + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + pj_list_push_back(&ioqueue->active_list, rec); +#endif + + pj_lock_release(ioqueue->lock); + + return PJ_SUCCESS; +} + + +/* + * pj_ioqueue_get_user_data() + */ +PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) +{ + PJ_ASSERT_RETURN(key, NULL); + return key->user_data; +} + +/* + * pj_ioqueue_set_user_data() + */ +PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key, + void *user_data, + void **old_data ) +{ + PJ_ASSERT_RETURN(key, PJ_EINVAL); + + if (old_data) + *old_data = key->user_data; + + key->user_data = user_data; + return PJ_SUCCESS; +} + + +#if PJ_IOQUEUE_HAS_SAFE_UNREG +/* Decrement the key's reference counter, and when the counter reach zero, + * destroy the key. + */ +static void decrement_counter(pj_ioqueue_key_t *key) +{ + if (pj_atomic_dec_and_get(key->ref_count) == 0) { + + pj_lock_acquire(key->ioqueue->lock); + + pj_assert(key->closing == 1); + pj_gettickcount(&key->free_time); + key->free_time.msec += PJ_IOQUEUE_KEY_FREE_DELAY; + pj_time_val_normalize(&key->free_time); + + pj_list_erase(key); + pj_list_push_back(&key->ioqueue->closing_list, key); + + pj_lock_release(key->ioqueue->lock); + } +} +#endif + +/* + * Poll the I/O Completion Port, execute callback, + * and return the key and bytes transfered of the last operation. + */ +static pj_bool_t poll_iocp( HANDLE hIocp, DWORD dwTimeout, + pj_ssize_t *p_bytes, pj_ioqueue_key_t **p_key ) +{ + DWORD dwBytesTransfered, dwKey; + generic_overlapped *pOv; + pj_ioqueue_key_t *key; + pj_ssize_t size_status = -1; + BOOL rcGetQueued; + + /* Poll for completion status. */ + rcGetQueued = GetQueuedCompletionStatus(hIocp, &dwBytesTransfered, + &dwKey, (OVERLAPPED**)&pOv, + dwTimeout); + + /* The return value is: + * - nonzero if event was dequeued. + * - zero and pOv==NULL if no event was dequeued. + * - zero and pOv!=NULL if event for failed I/O was dequeued. + */ + if (pOv) { + pj_bool_t has_lock; + + /* Event was dequeued for either successfull or failed I/O */ + key = (pj_ioqueue_key_t*)dwKey; + size_status = dwBytesTransfered; + + /* Report to caller regardless */ + if (p_bytes) + *p_bytes = size_status; + if (p_key) + *p_key = key; + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* We shouldn't call callbacks if key is quitting. */ + if (key->closing) + return PJ_TRUE; + + /* If concurrency is disabled, lock the key + * (and save the lock status to local var since app may change + * concurrency setting while in the callback) */ + if (key->allow_concurrent == PJ_FALSE) { + pj_mutex_lock(key->mutex); + has_lock = PJ_TRUE; + } else { + has_lock = PJ_FALSE; + } + + /* Now that we get the lock, check again that key is not closing */ + if (key->closing) { + if (has_lock) { + pj_mutex_unlock(key->mutex); + } + return PJ_TRUE; + } + + /* Increment reference counter to prevent this key from being + * deleted + */ + pj_atomic_inc(key->ref_count); +#else + PJ_UNUSED_ARG(has_lock); +#endif + + /* Carry out the callback */ + switch (pOv->operation) { + case PJ_IOQUEUE_OP_READ: + case PJ_IOQUEUE_OP_RECV: + case PJ_IOQUEUE_OP_RECV_FROM: + pOv->operation = 0; + if (key->cb.on_read_complete) + key->cb.on_read_complete(key, (pj_ioqueue_op_key_t*)pOv, + size_status); + break; + case PJ_IOQUEUE_OP_WRITE: + case PJ_IOQUEUE_OP_SEND: + case PJ_IOQUEUE_OP_SEND_TO: + pOv->operation = 0; + if (key->cb.on_write_complete) + key->cb.on_write_complete(key, (pj_ioqueue_op_key_t*)pOv, + size_status); + break; +#if PJ_HAS_TCP + case PJ_IOQUEUE_OP_ACCEPT: + /* special case for accept. */ + ioqueue_on_accept_complete(key, (ioqueue_accept_rec*)pOv); + if (key->cb.on_accept_complete) { + ioqueue_accept_rec *accept_rec = (ioqueue_accept_rec*)pOv; + pj_status_t status = PJ_SUCCESS; + pj_sock_t newsock; + + newsock = accept_rec->newsock; + accept_rec->newsock = PJ_INVALID_SOCKET; + + if (newsock == PJ_INVALID_SOCKET) { + int dwError = WSAGetLastError(); + if (dwError == 0) dwError = OSERR_ENOTCONN; + status = PJ_RETURN_OS_ERROR(dwError); + } + + key->cb.on_accept_complete(key, (pj_ioqueue_op_key_t*)pOv, + newsock, status); + + } + break; + case PJ_IOQUEUE_OP_CONNECT: +#endif + case PJ_IOQUEUE_OP_NONE: + pj_assert(0); + break; + } + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + decrement_counter(key); + if (has_lock) + pj_mutex_unlock(key->mutex); +#endif + + return PJ_TRUE; + } + + /* No event was queued. */ + return PJ_FALSE; +} + +/* + * pj_ioqueue_unregister() + */ +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key ) +{ + unsigned i; + pj_bool_t has_lock; + enum { RETRY = 10 }; + + PJ_ASSERT_RETURN(key, PJ_EINVAL); + +#if PJ_HAS_TCP + if (key->connecting) { + unsigned pos; + pj_ioqueue_t *ioqueue; + + ioqueue = key->ioqueue; + + /* Erase from connecting_handles */ + pj_lock_acquire(ioqueue->lock); + for (pos=0; pos < ioqueue->connecting_count; ++pos) { + if (ioqueue->connecting_keys[pos] == key) { + erase_connecting_socket(ioqueue, pos); + break; + } + } + key->connecting = 0; + pj_lock_release(ioqueue->lock); + } +#endif + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Mark key as closing before closing handle. */ + key->closing = 1; + + /* If concurrency is disabled, wait until the key has finished + * processing the callback + */ + if (key->allow_concurrent == PJ_FALSE) { + pj_mutex_lock(key->mutex); + has_lock = PJ_TRUE; + } else { + has_lock = PJ_FALSE; + } +#else + PJ_UNUSED_ARG(has_lock); +#endif + + /* Close handle (the only way to disassociate handle from IOCP). + * We also need to close handle to make sure that no further events + * will come to the handle. + */ + /* Update 2008/07/18 (http://trac.pjsip.org/repos/ticket/575): + * - It seems that CloseHandle() in itself does not actually close + * the socket (i.e. it will still appear in "netstat" output). Also + * if we only use CloseHandle(), an "Invalid Handle" exception will + * be raised in WSACleanup(). + * - MSDN documentation says that CloseHandle() must be called after + * closesocket() call (see + * http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx). + * But turns out that this will raise "Invalid Handle" exception + * in debug mode. + * So because of this, we replaced CloseHandle() with closesocket() + * instead. These was tested on WinXP SP2. + */ + //CloseHandle(key->hnd); + pj_sock_close((pj_sock_t)key->hnd); + + /* Reset callbacks */ + key->cb.on_accept_complete = NULL; + key->cb.on_connect_complete = NULL; + key->cb.on_read_complete = NULL; + key->cb.on_write_complete = NULL; + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Even after handle is closed, I suspect that IOCP may still try to + * do something with the handle, causing memory corruption when pool + * debugging is enabled. + * + * Forcing context switch seems to have fixed that, but this is quite + * an ugly solution.. + * + * Update 2008/02/13: + * This should not happen if concurrency is disallowed for the key. + * So at least application has a solution for this (i.e. by disallowing + * concurrency in the key). + */ + //This will loop forever if unregistration is done on the callback. + //Doing this with RETRY I think should solve the IOCP setting the + //socket signalled, without causing the deadlock. + //while (pj_atomic_get(key->ref_count) != 1) + // pj_thread_sleep(0); + for (i=0; pj_atomic_get(key->ref_count) != 1 && imutex); +#endif + + return PJ_SUCCESS; +} + +#if PJ_IOQUEUE_HAS_SAFE_UNREG +/* Scan the closing list, and put pending closing keys to free list. + * Must do this with ioqueue mutex held. + */ +static void scan_closing_keys(pj_ioqueue_t *ioqueue) +{ + if (!pj_list_empty(&ioqueue->closing_list)) { + pj_time_val now; + pj_ioqueue_key_t *key; + + pj_gettickcount(&now); + + /* Move closing keys to free list when they've finished the closing + * idle time. + */ + key = ioqueue->closing_list.next; + while (key != &ioqueue->closing_list) { + pj_ioqueue_key_t *next = key->next; + + pj_assert(key->closing != 0); + + if (PJ_TIME_VAL_GTE(now, key->free_time)) { + pj_list_erase(key); + pj_list_push_back(&ioqueue->free_list, key); + } + key = next; + } + } +} +#endif + +/* + * pj_ioqueue_poll() + * + * Poll for events. + */ +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout) +{ + DWORD dwMsec; +#if PJ_HAS_TCP + int connect_count = 0; +#endif + int event_count = 0; + + PJ_ASSERT_RETURN(ioqueue, -PJ_EINVAL); + + /* Calculate miliseconds timeout for GetQueuedCompletionStatus */ + dwMsec = timeout ? timeout->sec*1000 + timeout->msec : INFINITE; + + /* Poll for completion status. */ + event_count = poll_iocp(ioqueue->iocp, dwMsec, NULL, NULL); + +#if PJ_HAS_TCP + /* Check the connecting array, only when there's no activity. */ + if (event_count == 0) { + connect_count = check_connecting(ioqueue); + if (connect_count > 0) + event_count += connect_count; + } +#endif + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Check the closing keys only when there's no activity and when there are + * pending closing keys. + */ + if (event_count == 0 && !pj_list_empty(&ioqueue->closing_list)) { + pj_lock_acquire(ioqueue->lock); + scan_closing_keys(ioqueue); + pj_lock_release(ioqueue->lock); + } +#endif + + /* Return number of events. */ + return event_count; +} + +/* + * pj_ioqueue_recv() + * + * Initiate overlapped WSARecv() operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + void *buffer, + pj_ssize_t *length, + pj_uint32_t flags ) +{ + /* + * Ideally we should just call pj_ioqueue_recvfrom() with NULL addr and + * addrlen here. But unfortunately it generates EINVAL... :-( + * -bennylp + */ + int rc; + DWORD bytesRead; + DWORD dwFlags = 0; + union operation_key *op_key_rec; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL); + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Check key is not closing */ + if (key->closing) + return PJ_ECANCELLED; +#endif + + op_key_rec = (union operation_key*)op_key->internal__; + op_key_rec->overlapped.wsabuf.buf = buffer; + op_key_rec->overlapped.wsabuf.len = *length; + + dwFlags = flags; + + /* Try non-overlapped received first to see if data is + * immediately available. + */ + if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) { + rc = WSARecv((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, + &bytesRead, &dwFlags, NULL, NULL); + if (rc == 0) { + *length = bytesRead; + return PJ_SUCCESS; + } else { + DWORD dwError = WSAGetLastError(); + if (dwError != WSAEWOULDBLOCK) { + *length = -1; + return PJ_RETURN_OS_ERROR(dwError); + } + } + } + + dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); + + /* + * No immediate data available. + * Register overlapped Recv() operation. + */ + pj_bzero( &op_key_rec->overlapped.overlapped, + sizeof(op_key_rec->overlapped.overlapped)); + op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV; + + rc = WSARecv((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, + &bytesRead, &dwFlags, + &op_key_rec->overlapped.overlapped, NULL); + if (rc == SOCKET_ERROR) { + DWORD dwStatus = WSAGetLastError(); + if (dwStatus!=WSA_IO_PENDING) { + *length = -1; + return PJ_STATUS_FROM_OS(dwStatus); + } + } + + /* Pending operation has been scheduled. */ + return PJ_EPENDING; +} + +/* + * pj_ioqueue_recvfrom() + * + * Initiate overlapped RecvFrom() operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + void *buffer, + pj_ssize_t *length, + pj_uint32_t flags, + pj_sockaddr_t *addr, + int *addrlen) +{ + int rc; + DWORD bytesRead; + DWORD dwFlags = 0; + union operation_key *op_key_rec; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(key && op_key && buffer, PJ_EINVAL); + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Check key is not closing */ + if (key->closing) + return PJ_ECANCELLED; +#endif + + op_key_rec = (union operation_key*)op_key->internal__; + op_key_rec->overlapped.wsabuf.buf = buffer; + op_key_rec->overlapped.wsabuf.len = *length; + + dwFlags = flags; + + /* Try non-overlapped received first to see if data is + * immediately available. + */ + if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) { + rc = WSARecvFrom((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, + &bytesRead, &dwFlags, addr, addrlen, NULL, NULL); + if (rc == 0) { + *length = bytesRead; + return PJ_SUCCESS; + } else { + DWORD dwError = WSAGetLastError(); + if (dwError != WSAEWOULDBLOCK) { + *length = -1; + return PJ_RETURN_OS_ERROR(dwError); + } + } + } + + dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); + + /* + * No immediate data available. + * Register overlapped Recv() operation. + */ + pj_bzero( &op_key_rec->overlapped.overlapped, + sizeof(op_key_rec->overlapped.overlapped)); + op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV; + + rc = WSARecvFrom((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, + &bytesRead, &dwFlags, addr, addrlen, + &op_key_rec->overlapped.overlapped, NULL); + if (rc == SOCKET_ERROR) { + DWORD dwStatus = WSAGetLastError(); + if (dwStatus!=WSA_IO_PENDING) { + *length = -1; + return PJ_STATUS_FROM_OS(dwStatus); + } + } + + /* Pending operation has been scheduled. */ + return PJ_EPENDING; +} + +/* + * pj_ioqueue_send() + * + * Initiate overlapped Send operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + const void *data, + pj_ssize_t *length, + pj_uint32_t flags ) +{ + return pj_ioqueue_sendto(key, op_key, data, length, flags, NULL, 0); +} + + +/* + * pj_ioqueue_sendto() + * + * Initiate overlapped SendTo operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + const void *data, + pj_ssize_t *length, + pj_uint32_t flags, + const pj_sockaddr_t *addr, + int addrlen) +{ + int rc; + DWORD bytesWritten; + DWORD dwFlags; + union operation_key *op_key_rec; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(key && op_key && data, PJ_EINVAL); + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Check key is not closing */ + if (key->closing) + return PJ_ECANCELLED; +#endif + + op_key_rec = (union operation_key*)op_key->internal__; + + /* + * First try blocking write. + */ + op_key_rec->overlapped.wsabuf.buf = (void*)data; + op_key_rec->overlapped.wsabuf.len = *length; + + dwFlags = flags; + + if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) { + rc = WSASendTo((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, + &bytesWritten, dwFlags, addr, addrlen, + NULL, NULL); + if (rc == 0) { + *length = bytesWritten; + return PJ_SUCCESS; + } else { + DWORD dwStatus = WSAGetLastError(); + if (dwStatus != WSAEWOULDBLOCK) { + *length = -1; + return PJ_RETURN_OS_ERROR(dwStatus); + } + } + } + + dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); + + /* + * Data can't be sent immediately. + * Schedule asynchronous WSASend(). + */ + pj_bzero( &op_key_rec->overlapped.overlapped, + sizeof(op_key_rec->overlapped.overlapped)); + op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_SEND; + + rc = WSASendTo((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, + &bytesWritten, dwFlags, addr, addrlen, + &op_key_rec->overlapped.overlapped, NULL); + if (rc == SOCKET_ERROR) { + DWORD dwStatus = WSAGetLastError(); + if (dwStatus!=WSA_IO_PENDING) + return PJ_STATUS_FROM_OS(dwStatus); + } + + /* Asynchronous operation successfully submitted. */ + return PJ_EPENDING; +} + +#if PJ_HAS_TCP + +/* + * pj_ioqueue_accept() + * + * Initiate overlapped accept() operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen) +{ + BOOL rc; + DWORD bytesReceived; + pj_status_t status; + union operation_key *op_key_rec; + SOCKET sock; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL); + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Check key is not closing */ + if (key->closing) + return PJ_ECANCELLED; +#endif + + /* + * See if there is a new connection immediately available. + */ + sock = WSAAccept((SOCKET)key->hnd, remote, addrlen, NULL, 0); + if (sock != INVALID_SOCKET) { + /* Yes! New socket is available! */ + if (local && addrlen) { + int status; + + /* On WinXP or later, use SO_UPDATE_ACCEPT_CONTEXT so that socket + * addresses can be obtained with getsockname() and getpeername(). + */ + status = setsockopt(sock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + (char*)&key->hnd, sizeof(SOCKET)); + /* SO_UPDATE_ACCEPT_CONTEXT is for WinXP or later. + * So ignore the error status. + */ + + status = getsockname(sock, local, addrlen); + if (status != 0) { + DWORD dwError = WSAGetLastError(); + closesocket(sock); + return PJ_RETURN_OS_ERROR(dwError); + } + } + + *new_sock = sock; + return PJ_SUCCESS; + + } else { + DWORD dwError = WSAGetLastError(); + if (dwError != WSAEWOULDBLOCK) { + return PJ_RETURN_OS_ERROR(dwError); + } + } + + /* + * No connection is immediately available. + * Must schedule an asynchronous operation. + */ + op_key_rec = (union operation_key*)op_key->internal__; + + status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, + &op_key_rec->accept.newsock); + if (status != PJ_SUCCESS) + return status; + + op_key_rec->accept.operation = PJ_IOQUEUE_OP_ACCEPT; + op_key_rec->accept.addrlen = addrlen; + op_key_rec->accept.local = local; + op_key_rec->accept.remote = remote; + op_key_rec->accept.newsock_ptr = new_sock; + pj_bzero( &op_key_rec->accept.overlapped, + sizeof(op_key_rec->accept.overlapped)); + + rc = AcceptEx( (SOCKET)key->hnd, (SOCKET)op_key_rec->accept.newsock, + op_key_rec->accept.accept_buf, + 0, ACCEPT_ADDR_LEN, ACCEPT_ADDR_LEN, + &bytesReceived, + &op_key_rec->accept.overlapped ); + + if (rc == TRUE) { + ioqueue_on_accept_complete(key, &op_key_rec->accept); + return PJ_SUCCESS; + } else { + DWORD dwStatus = WSAGetLastError(); + if (dwStatus!=WSA_IO_PENDING) + return PJ_STATUS_FROM_OS(dwStatus); + } + + /* Asynchronous Accept() has been submitted. */ + return PJ_EPENDING; +} + + +/* + * pj_ioqueue_connect() + * + * Initiate overlapped connect() operation (well, it's non-blocking actually, + * since there's no overlapped version of connect()). + */ +PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ) +{ + HANDLE hEvent; + pj_ioqueue_t *ioqueue; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL); + +#if PJ_IOQUEUE_HAS_SAFE_UNREG + /* Check key is not closing */ + if (key->closing) + return PJ_ECANCELLED; +#endif + + /* Initiate connect() */ + if (connect((pj_sock_t)key->hnd, addr, addrlen) != 0) { + DWORD dwStatus; + dwStatus = WSAGetLastError(); + if (dwStatus != WSAEWOULDBLOCK) { + return PJ_RETURN_OS_ERROR(dwStatus); + } + } else { + /* Connect has completed immediately! */ + return PJ_SUCCESS; + } + + ioqueue = key->ioqueue; + + /* Add to the array of connecting socket to be polled */ + pj_lock_acquire(ioqueue->lock); + + if (ioqueue->connecting_count >= MAXIMUM_WAIT_OBJECTS) { + pj_lock_release(ioqueue->lock); + return PJ_ETOOMANYCONN; + } + + /* Get or create event object. */ + if (ioqueue->event_count) { + hEvent = ioqueue->event_pool[ioqueue->event_count - 1]; + --ioqueue->event_count; + } else { + hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (hEvent == NULL) { + DWORD dwStatus = GetLastError(); + pj_lock_release(ioqueue->lock); + return PJ_STATUS_FROM_OS(dwStatus); + } + } + + /* Mark key as connecting. + * We can't use array index since key can be removed dynamically. + */ + key->connecting = 1; + + /* Associate socket events to the event object. */ + if (WSAEventSelect((pj_sock_t)key->hnd, hEvent, FD_CONNECT) != 0) { + CloseHandle(hEvent); + pj_lock_release(ioqueue->lock); + return PJ_RETURN_OS_ERROR(WSAGetLastError()); + } + + /* Add to array. */ + ioqueue->connecting_keys[ ioqueue->connecting_count ] = key; + ioqueue->connecting_handles[ ioqueue->connecting_count ] = hEvent; + ioqueue->connecting_count++; + + pj_lock_release(ioqueue->lock); + + return PJ_EPENDING; +} +#endif /* #if PJ_HAS_TCP */ + + +PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key, + pj_size_t size ) +{ + pj_bzero(op_key, size); +} + +PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key ) +{ + BOOL rc; + DWORD bytesTransfered; + + rc = GetOverlappedResult( key->hnd, (LPOVERLAPPED)op_key, + &bytesTransfered, FALSE ); + + if (rc == FALSE) { + return GetLastError()==ERROR_IO_INCOMPLETE; + } + + return FALSE; +} + + +PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_status ) +{ + BOOL rc; + + rc = PostQueuedCompletionStatus(key->ioqueue->iocp, bytes_status, + (long)key, (OVERLAPPED*)op_key ); + if (rc == FALSE) { + return PJ_RETURN_OS_ERROR(GetLastError()); + } + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_ioqueue_set_concurrency(pj_ioqueue_key_t *key, + pj_bool_t allow) +{ + PJ_ASSERT_RETURN(key, PJ_EINVAL); + + /* PJ_IOQUEUE_HAS_SAFE_UNREG must be enabled if concurrency is + * disabled. + */ + PJ_ASSERT_RETURN(allow || PJ_IOQUEUE_HAS_SAFE_UNREG, PJ_EINVAL); + + key->allow_concurrent = allow; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_ioqueue_lock_key(pj_ioqueue_key_t *key) +{ +#if PJ_IOQUEUE_HAS_SAFE_UNREG + return pj_mutex_lock(key->mutex); +#else + PJ_ASSERT_RETURN(!"PJ_IOQUEUE_HAS_SAFE_UNREG is disabled", PJ_EINVALIDOP); +#endif +} + +PJ_DEF(pj_status_t) pj_ioqueue_unlock_key(pj_ioqueue_key_t *key) +{ +#if PJ_IOQUEUE_HAS_SAFE_UNREG + return pj_mutex_unlock(key->mutex); +#else + PJ_ASSERT_RETURN(!"PJ_IOQUEUE_HAS_SAFE_UNREG is disabled", PJ_EINVALIDOP); +#endif +} + diff --git a/pjlib/src/pj/ip_helper_generic.c b/pjlib/src/pj/ip_helper_generic.c new file mode 100644 index 0000000..3a43423 --- /dev/null +++ b/pjlib/src/pj/ip_helper_generic.c @@ -0,0 +1,401 @@ +/* $Id: ip_helper_generic.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include + +/* Set to 1 to enable tracing */ +#if 0 +# include +# define THIS_FILE "ip_helper_generic.c" +# define TRACE_(exp) PJ_LOG(5,exp) + static const char *get_os_errmsg(void) + { + static char errmsg[PJ_ERR_MSG_SIZE]; + pj_strerror(pj_get_os_error(), errmsg, sizeof(errmsg)); + return errmsg; + } + static const char *get_addr(void *addr) + { + static char txt[PJ_INET6_ADDRSTRLEN]; + struct sockaddr *ad = (struct sockaddr*)addr; + if (ad->sa_family != PJ_AF_INET && ad->sa_family != PJ_AF_INET6) + return "?"; + return pj_inet_ntop2(ad->sa_family, pj_sockaddr_get_addr(ad), + txt, sizeof(txt)); + } +#else +# define TRACE_(exp) +#endif + + +#if 0 + /* dummy */ + +#elif defined(PJ_HAS_IFADDRS_H) && PJ_HAS_IFADDRS_H != 0 && \ + defined(PJ_HAS_NET_IF_H) && PJ_HAS_NET_IF_H != 0 +/* Using getifaddrs() is preferred since it can work with both IPv4 and IPv6 */ +static pj_status_t if_enum_by_af(int af, + unsigned *p_cnt, + pj_sockaddr ifs[]) +{ + struct ifaddrs *ifap = NULL, *it; + unsigned max; + + PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL); + + TRACE_((THIS_FILE, "Starting interface enum with getifaddrs() for af=%d", + af)); + + if (getifaddrs(&ifap) != 0) { + TRACE_((THIS_FILE, " getifarrds() failed: %s", get_os_errmsg())); + return PJ_RETURN_OS_ERROR(pj_get_netos_error()); + } + + it = ifap; + max = *p_cnt; + *p_cnt = 0; + for (; it!=NULL && *p_cnt < max; it = it->ifa_next) { + struct sockaddr *ad = it->ifa_addr; + + TRACE_((THIS_FILE, " checking %s", it->ifa_name)); + + if ((it->ifa_flags & IFF_UP)==0) { + TRACE_((THIS_FILE, " interface is down")); + continue; /* Skip when interface is down */ + } + +#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF + if (it->ifa_flags & IFF_LOOPBACK) { + TRACE_((THIS_FILE, " loopback interface")); + continue; /* Skip loopback interface */ + } +#endif + + if (ad==NULL) { + TRACE_((THIS_FILE, " NULL address ignored")); + continue; /* reported to happen on Linux 2.6.25.9 + with ppp interface */ + } + + if (ad->sa_family != af) { + TRACE_((THIS_FILE, " address %s ignored (af=%d)", + get_addr(ad), ad->sa_family)); + continue; /* Skip when interface is down */ + } + + /* Ignore 0.0.0.0/8 address. This is a special address + * which doesn't seem to have practical use. + */ + if (af==pj_AF_INET() && + (pj_ntohl(((pj_sockaddr_in*)ad)->sin_addr.s_addr) >> 24) == 0) + { + TRACE_((THIS_FILE, " address %s ignored (0.0.0.0/8 class)", + get_addr(ad), ad->sa_family)); + continue; + } + + TRACE_((THIS_FILE, " address %s (af=%d) added at index %d", + get_addr(ad), ad->sa_family, *p_cnt)); + + pj_bzero(&ifs[*p_cnt], sizeof(ifs[0])); + pj_memcpy(&ifs[*p_cnt], ad, pj_sockaddr_get_len(ad)); + PJ_SOCKADDR_RESET_LEN(&ifs[*p_cnt]); + (*p_cnt)++; + } + + freeifaddrs(ifap); + TRACE_((THIS_FILE, "done, found %d address(es)", *p_cnt)); + return (*p_cnt != 0) ? PJ_SUCCESS : PJ_ENOTFOUND; +} + +#elif defined(SIOCGIFCONF) && \ + defined(PJ_HAS_NET_IF_H) && PJ_HAS_NET_IF_H != 0 + +/* Note: this does not work with IPv6 */ +static pj_status_t if_enum_by_af(int af, + unsigned *p_cnt, + pj_sockaddr ifs[]) +{ + pj_sock_t sock; + char buf[512]; + struct ifconf ifc; + struct ifreq *ifr; + int i, count; + pj_status_t status; + + PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL); + + TRACE_((THIS_FILE, "Starting interface enum with SIOCGIFCONF for af=%d", + af)); + + status = pj_sock_socket(af, PJ_SOCK_DGRAM, 0, &sock); + if (status != PJ_SUCCESS) + return status; + + /* Query available interfaces */ + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + + if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { + int oserr = pj_get_netos_error(); + TRACE_((THIS_FILE, " ioctl(SIOCGIFCONF) failed: %s", get_os_errmsg())); + pj_sock_close(sock); + return PJ_RETURN_OS_ERROR(oserr); + } + + /* Done with socket */ + pj_sock_close(sock); + + /* Interface interfaces */ + ifr = (struct ifreq*) ifc.ifc_req; + count = ifc.ifc_len / sizeof(struct ifreq); + if (count > *p_cnt) + count = *p_cnt; + + *p_cnt = 0; + for (i=0; iifr_addr; + + TRACE_((THIS_FILE, " checking interface %s", itf->ifr_name)); + + /* Skip address with different family */ + if (ad->sa_family != af) { + TRACE_((THIS_FILE, " address %s (af=%d) ignored", + get_addr(ad), (int)ad->sa_family)); + continue; + } + + if ((itf->ifr_flags & IFF_UP)==0) { + TRACE_((THIS_FILE, " interface is down")); + continue; /* Skip when interface is down */ + } + +#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF + if (itf->ifr_flags & IFF_LOOPBACK) { + TRACE_((THIS_FILE, " loopback interface")); + continue; /* Skip loopback interface */ + } +#endif + + /* Ignore 0.0.0.0/8 address. This is a special address + * which doesn't seem to have practical use. + */ + if (af==pj_AF_INET() && + (pj_ntohl(((pj_sockaddr_in*)ad)->sin_addr.s_addr) >> 24) == 0) + { + TRACE_((THIS_FILE, " address %s ignored (0.0.0.0/8 class)", + get_addr(ad), ad->sa_family)); + continue; + } + + TRACE_((THIS_FILE, " address %s (af=%d) added at index %d", + get_addr(ad), ad->sa_family, *p_cnt)); + + pj_bzero(&ifs[*p_cnt], sizeof(ifs[0])); + pj_memcpy(&ifs[*p_cnt], ad, pj_sockaddr_get_len(ad)); + PJ_SOCKADDR_RESET_LEN(&ifs[*p_cnt]); + (*p_cnt)++; + } + + TRACE_((THIS_FILE, "done, found %d address(es)", *p_cnt)); + return (*p_cnt != 0) ? PJ_SUCCESS : PJ_ENOTFOUND; +} + +#elif defined(PJ_HAS_NET_IF_H) && PJ_HAS_NET_IF_H != 0 +/* Note: this does not work with IPv6 */ +static pj_status_t if_enum_by_af(int af, unsigned *p_cnt, pj_sockaddr ifs[]) +{ + struct if_nameindex *if_list; + struct ifreq ifreq; + pj_sock_t sock; + unsigned i, max_count; + pj_status_t status; + + PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL); + + TRACE_((THIS_FILE, "Starting if_nameindex() for af=%d", af)); + + status = pj_sock_socket(af, PJ_SOCK_DGRAM, 0, &sock); + if (status != PJ_SUCCESS) + return status; + + if_list = if_nameindex(); + if (if_list == NULL) + return PJ_ENOTFOUND; + + max_count = *p_cnt; + *p_cnt = 0; + for (i=0; if_list[i].if_index && *p_cntsa_family != af) { + TRACE_((THIS_FILE, " address %s family %d ignored", + get_addr(&ifreq.ifr_addr), + ifreq.ifr_addr.sa_family)); + continue; /* Not address family that we want, continue */ + } + + /* Ignore 0.0.0.0/8 address. This is a special address + * which doesn't seem to have practical use. + */ + if (af==pj_AF_INET() && + (pj_ntohl(((pj_sockaddr_in*)ad)->sin_addr.s_addr) >> 24) == 0) + { + TRACE_((THIS_FILE, " address %s ignored (0.0.0.0/8 class)", + get_addr(ad), ad->sa_family)); + continue; + } + + /* Got an address ! */ + TRACE_((THIS_FILE, " address %s (af=%d) added at index %d", + get_addr(ad), ad->sa_family, *p_cnt)); + + pj_bzero(&ifs[*p_cnt], sizeof(ifs[0])); + pj_memcpy(&ifs[*p_cnt], ad, pj_sockaddr_get_len(ad)); + PJ_SOCKADDR_RESET_LEN(&ifs[*p_cnt]); + (*p_cnt)++; + } + + if_freenameindex(if_list); + pj_sock_close(sock); + + TRACE_((THIS_FILE, "done, found %d address(es)", *p_cnt)); + return (*p_cnt != 0) ? PJ_SUCCESS : PJ_ENOTFOUND; +} + +#else +static pj_status_t if_enum_by_af(int af, + unsigned *p_cnt, + pj_sockaddr ifs[]) +{ + pj_status_t status; + + PJ_ASSERT_RETURN(p_cnt && *p_cnt > 0 && ifs, PJ_EINVAL); + + pj_bzero(ifs, sizeof(ifs[0]) * (*p_cnt)); + + /* Just get one default route */ + status = pj_getdefaultipinterface(af, &ifs[0]); + if (status != PJ_SUCCESS) + return status; + + *p_cnt = 1; + return PJ_SUCCESS; +} +#endif /* SIOCGIFCONF */ + +/* + * Enumerate the local IP interface currently active in the host. + */ +PJ_DEF(pj_status_t) pj_enum_ip_interface(int af, + unsigned *p_cnt, + pj_sockaddr ifs[]) +{ + unsigned start; + pj_status_t status; + + start = 0; + if (af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) { + unsigned max = *p_cnt; + status = if_enum_by_af(PJ_AF_INET6, &max, &ifs[start]); + if (status == PJ_SUCCESS) { + start += max; + (*p_cnt) -= max; + } + } + + if (af==PJ_AF_INET || af==PJ_AF_UNSPEC) { + unsigned max = *p_cnt; + status = if_enum_by_af(PJ_AF_INET, &max, &ifs[start]); + if (status == PJ_SUCCESS) { + start += max; + (*p_cnt) -= max; + } + } + + *p_cnt = start; + + return (*p_cnt != 0) ? PJ_SUCCESS : PJ_ENOTFOUND; +} + +/* + * Enumerate the IP routing table for this host. + */ +PJ_DEF(pj_status_t) pj_enum_ip_route(unsigned *p_cnt, + pj_ip_route_entry routes[]) +{ + pj_sockaddr itf; + pj_status_t status; + + PJ_ASSERT_RETURN(p_cnt && *p_cnt > 0 && routes, PJ_EINVAL); + + pj_bzero(routes, sizeof(routes[0]) * (*p_cnt)); + + /* Just get one default route */ + status = pj_getdefaultipinterface(PJ_AF_INET, &itf); + if (status != PJ_SUCCESS) + return status; + + routes[0].ipv4.if_addr.s_addr = itf.ipv4.sin_addr.s_addr; + routes[0].ipv4.dst_addr.s_addr = 0; + routes[0].ipv4.mask.s_addr = 0; + *p_cnt = 1; + + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/ip_helper_symbian.cpp b/pjlib/src/pj/ip_helper_symbian.cpp new file mode 100644 index 0000000..fb751ad --- /dev/null +++ b/pjlib/src/pj/ip_helper_symbian.cpp @@ -0,0 +1,150 @@ +/* $Id: ip_helper_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include + + +#include "os_symbian.h" + +#define THIS_FILE "ip_helper_symbian.cpp" +#define TRACE_ME 0 + +static pj_status_t rsock_enum_interface(int af, + unsigned *p_cnt, + pj_sockaddr ifs[]) +{ + TInt rc; + RSocket rSock; + TPckgBuf info; + unsigned i; + + if (PjSymbianOS::Instance()->Connection()) { + + rc = rSock.Open(PjSymbianOS::Instance()->SocketServ(), + af, PJ_SOCK_DGRAM, KProtocolInetUdp, + *PjSymbianOS::Instance()->Connection()); + } else { + + rc = rSock.Open(PjSymbianOS::Instance()->SocketServ(), + af, PJ_SOCK_DGRAM, KProtocolInetUdp); + + } + + if (rc != KErrNone) + return PJ_RETURN_OS_ERROR(rc); + + rSock.SetOpt(KSoInetEnumInterfaces, KSolInetIfCtrl); + + for (i=0; i<*p_cnt && + rSock.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, + info) == KErrNone; ) + { + TInetAddr &iAddress = info().iAddress; + int namelen; + +#if TRACE_ME + if (1) { + pj_sockaddr a; + char ipaddr[PJ_INET6_ADDRSTRLEN+2]; + + namelen = sizeof(pj_sockaddr); + if (PjSymbianOS::Addr2pj(iAddress, a, &namelen, + PJ_FALSE) == PJ_SUCCESS) + { + PJ_LOG(5,(THIS_FILE, "Enum: found address %s", + pj_sockaddr_print(&a, ipaddr, sizeof(ipaddr), 2))); + } + } +#endif + + namelen = sizeof(ifs[i]); + if (PjSymbianOS::Addr2pj(iAddress, ifs[i], &namelen, + PJ_TRUE) != PJ_SUCCESS) + { + continue; + } + + if (ifs[i].addr.sa_family != af) + continue; + + ++i; + } + + rSock.Close(); + + // Done + *p_cnt = i; + + return PJ_SUCCESS; +} + +/* + * Enumerate the local IP interface currently active in the host. + */ +PJ_DEF(pj_status_t) pj_enum_ip_interface(int af, + unsigned *p_cnt, + pj_sockaddr ifs[]) +{ + unsigned start; + pj_status_t status = PJ_SUCCESS; + + start = 0; + + /* Get IPv6 interface first. */ + if (af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) { + unsigned max = *p_cnt; + status = rsock_enum_interface(PJ_AF_INET6, &max, &ifs[start]); + if (status == PJ_SUCCESS) { + (*p_cnt) -= max; + start += max; + } + } + + /* Get IPv4 interface. */ + if (af==PJ_AF_INET || af==PJ_AF_UNSPEC) { + unsigned max = *p_cnt; + status = rsock_enum_interface(PJ_AF_INET, &max, &ifs[start]); + if (status == PJ_SUCCESS) { + (*p_cnt) -= max; + start += max; + } + } + + *p_cnt = start; + + return start ? PJ_SUCCESS : PJ_ENOTFOUND; +} + +/* + * Enumerate the IP routing table for this host. + */ +PJ_DEF(pj_status_t) pj_enum_ip_route(unsigned *p_cnt, + pj_ip_route_entry routes[]) +{ + PJ_ASSERT_RETURN(p_cnt && *p_cnt > 0 && routes, PJ_EINVAL); + *p_cnt = 0; + return PJ_ENOTSUP; +} + diff --git a/pjlib/src/pj/ip_helper_win32.c b/pjlib/src/pj/ip_helper_win32.c new file mode 100644 index 0000000..a924606 --- /dev/null +++ b/pjlib/src/pj/ip_helper_win32.c @@ -0,0 +1,441 @@ +/* $Id: ip_helper_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + +#define WIN32_LEAN_AND_MEAN +#include + +/* PMIB_ICMP_EX is not declared in VC6, causing error. + * But EVC4, which also claims to be VC6, does have it! + */ +#if defined(_MSC_VER) && _MSC_VER==1200 && !defined(PJ_WIN32_WINCE) +# define PMIB_ICMP_EX void* +#endif +#include + +/* If you encounter error "Cannot open include file: 'Iphlpapi.h' here, + * you need to install newer Platform SDK. Presumably you're using + * Microsoft Visual Studio 6? + */ +#include + +#include +#include +#include +#include + +/* Dealing with Unicode quirks: + + There seems to be a difference with GetProcAddress() API signature between + Windows (i.e. Win32) and Windows CE (e.g. Windows Mobile). On Windows, the + API is declared as: + + FARPROC GetProcAddress( + HMODULE hModule, + LPCSTR lpProcName); + + while on Windows CE: + + FARPROC GetProcAddress( + HMODULE hModule, + LPCWSTR lpProcName); + + Notice the difference with lpProcName argument type. This means that on + Windows, even on Unicode Windows, the lpProcName always takes ANSI format, + while on Windows CE, the argument follows the UNICODE setting. + + Because of this, we use a different Unicode treatment here than the usual + PJ_NATIVE_STRING_IS_UNICODE PJLIB setting (): + - GPA_TEXT macro: convert literal string to platform's native literal + string + - gpa_char: the platform native character type + + Note that "GPA" and "gpa" are abbreviations for GetProcAddress. +*/ +#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 + /* on CE, follow the PJLIB Unicode setting */ +# define GPA_TEXT(x) PJ_T(x) +# define gpa_char pj_char_t +#else + /* on non-CE, always use ANSI format */ +# define GPA_TEXT(x) x +# define gpa_char char +#endif + + +typedef DWORD (WINAPI *PFN_GetIpAddrTable)(PMIB_IPADDRTABLE pIpAddrTable, + PULONG pdwSize, + BOOL bOrder); +typedef DWORD (WINAPI *PFN_GetAdapterAddresses)(ULONG Family, + ULONG Flags, + PVOID Reserved, + PIP_ADAPTER_ADDRESSES AdapterAddresses, + PULONG SizePointer); +typedef DWORD (WINAPI *PFN_GetIpForwardTable)(PMIB_IPFORWARDTABLE pIpForwardTable, + PULONG pdwSize, + BOOL bOrder); +typedef DWORD (WINAPI *PFN_GetIfEntry)(PMIB_IFROW pIfRow); + +static HANDLE s_hDLL; +static PFN_GetIpAddrTable s_pfnGetIpAddrTable; +static PFN_GetAdapterAddresses s_pfnGetAdapterAddresses; +static PFN_GetIpForwardTable s_pfnGetIpForwardTable; +static PFN_GetIfEntry s_pfnGetIfEntry; + + +static void unload_iphlp_module(void) +{ + FreeLibrary(s_hDLL); + s_hDLL = NULL; + s_pfnGetIpAddrTable = NULL; + s_pfnGetIpForwardTable = NULL; + s_pfnGetIfEntry = NULL; + s_pfnGetAdapterAddresses = NULL; +} + +static FARPROC GetIpHlpApiProc(gpa_char *lpProcName) +{ + if(NULL == s_hDLL) { + s_hDLL = LoadLibrary(PJ_T("IpHlpApi")); + if(NULL != s_hDLL) { + pj_atexit(&unload_iphlp_module); + } + } + + if(NULL != s_hDLL) + return GetProcAddress(s_hDLL, lpProcName); + + return NULL; +} + +static DWORD MyGetIpAddrTable(PMIB_IPADDRTABLE pIpAddrTable, + PULONG pdwSize, + BOOL bOrder) +{ + if(NULL == s_pfnGetIpAddrTable) { + s_pfnGetIpAddrTable = (PFN_GetIpAddrTable) + GetIpHlpApiProc(GPA_TEXT("GetIpAddrTable")); + } + + if(NULL != s_pfnGetIpAddrTable) { + return s_pfnGetIpAddrTable(pIpAddrTable, pdwSize, bOrder); + } + + return ERROR_NOT_SUPPORTED; +} + +static DWORD MyGetAdapterAddresses(ULONG Family, + ULONG Flags, + PVOID Reserved, + PIP_ADAPTER_ADDRESSES AdapterAddresses, + PULONG SizePointer) +{ + if(NULL == s_pfnGetAdapterAddresses) { + s_pfnGetAdapterAddresses = (PFN_GetAdapterAddresses) + GetIpHlpApiProc(GPA_TEXT("GetAdaptersAddresses")); + } + + if(NULL != s_pfnGetAdapterAddresses) { + return s_pfnGetAdapterAddresses(Family, Flags, Reserved, + AdapterAddresses, SizePointer); + } + + return ERROR_NOT_SUPPORTED; +} + +#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF +static DWORD MyGetIfEntry(MIB_IFROW *pIfRow) +{ + if(NULL == s_pfnGetIfEntry) { + s_pfnGetIfEntry = (PFN_GetIfEntry) + GetIpHlpApiProc(GPA_TEXT("GetIfEntry")); + } + + if(NULL != s_pfnGetIfEntry) { + return s_pfnGetIfEntry(pIfRow); + } + + return ERROR_NOT_SUPPORTED; +} +#endif + + +static DWORD MyGetIpForwardTable(PMIB_IPFORWARDTABLE pIpForwardTable, + PULONG pdwSize, + BOOL bOrder) +{ + if(NULL == s_pfnGetIpForwardTable) { + s_pfnGetIpForwardTable = (PFN_GetIpForwardTable) + GetIpHlpApiProc(GPA_TEXT("GetIpForwardTable")); + } + + if(NULL != s_pfnGetIpForwardTable) { + return s_pfnGetIpForwardTable(pIpForwardTable, pdwSize, bOrder); + } + + return ERROR_NOT_SUPPORTED; +} + +/* Enumerate local IP interface using GetIpAddrTable() + * for IPv4 addresses only. + */ +static pj_status_t enum_ipv4_interface(unsigned *p_cnt, + pj_sockaddr ifs[]) +{ + char ipTabBuff[512]; + MIB_IPADDRTABLE *pTab = (MIB_IPADDRTABLE*)ipTabBuff; + ULONG tabSize = sizeof(ipTabBuff); + unsigned i, count; + DWORD rc = NO_ERROR; + + PJ_ASSERT_RETURN(p_cnt && ifs, PJ_EINVAL); + + /* Get IP address table */ + rc = MyGetIpAddrTable(pTab, &tabSize, FALSE); + if (rc != NO_ERROR) { + if (rc == ERROR_INSUFFICIENT_BUFFER) { + /* Retry with larger buffer */ + pTab = (MIB_IPADDRTABLE*)malloc(tabSize); + if (pTab) + rc = MyGetIpAddrTable(pTab, &tabSize, FALSE); + } + + if (rc != NO_ERROR) { + if (pTab != (MIB_IPADDRTABLE*)ipTabBuff) + free(pTab); + return PJ_RETURN_OS_ERROR(rc); + } + } + + /* Reset result */ + pj_bzero(ifs, sizeof(ifs[0]) * (*p_cnt)); + + /* Now fill out the entries */ + count = (pTab->dwNumEntries < *p_cnt) ? pTab->dwNumEntries : *p_cnt; + *p_cnt = 0; + for (i=0; itable[i].dwAddr == 0) + continue; + + /* Ignore 0.0.0.0/8 address. This is a special address + * which doesn't seem to have practical use. + */ + if ((pj_ntohl(pTab->table[i].dwAddr) >> 24) == 0) + continue; + +#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF + /* Investigate the type of this interface */ + pj_bzero(&ifRow, sizeof(ifRow)); + ifRow.dwIndex = pTab->table[i].dwIndex; + if (MyGetIfEntry(&ifRow) != 0) + continue; + + if (ifRow.dwType == MIB_IF_TYPE_LOOPBACK) + continue; +#endif + + ifs[*p_cnt].ipv4.sin_family = PJ_AF_INET; + ifs[*p_cnt].ipv4.sin_addr.s_addr = pTab->table[i].dwAddr; + (*p_cnt)++; + } + + if (pTab != (MIB_IPADDRTABLE*)ipTabBuff) + free(pTab); + + return (*p_cnt) ? PJ_SUCCESS : PJ_ENOTFOUND; +} + +/* Enumerate local IP interface using GetAdapterAddresses(), + * which works for both IPv4 and IPv6. + */ +static pj_status_t enum_ipv4_ipv6_interface(int af, + unsigned *p_cnt, + pj_sockaddr ifs[]) +{ + pj_uint8_t buffer[600]; + IP_ADAPTER_ADDRESSES *adapter = (IP_ADAPTER_ADDRESSES*)buffer; + void *adapterBuf = NULL; + ULONG size = sizeof(buffer); + ULONG flags; + unsigned i; + DWORD rc; + + flags = GAA_FLAG_SKIP_FRIENDLY_NAME | + GAA_FLAG_SKIP_DNS_SERVER | + GAA_FLAG_SKIP_MULTICAST; + + rc = MyGetAdapterAddresses(af, flags, NULL, adapter, &size); + if (rc != ERROR_SUCCESS) { + if (rc == ERROR_BUFFER_OVERFLOW) { + /* Retry with larger memory size */ + adapterBuf = malloc(size); + adapter = (IP_ADAPTER_ADDRESSES*) adapterBuf; + if (adapter != NULL) + rc = MyGetAdapterAddresses(af, flags, NULL, adapter, &size); + } + + if (rc != ERROR_SUCCESS) { + if (adapterBuf) + free(adapterBuf); + return PJ_RETURN_OS_ERROR(rc); + } + } + + /* Reset result */ + pj_bzero(ifs, sizeof(ifs[0]) * (*p_cnt)); + + /* Enumerate interface */ + for (i=0; i<*p_cnt && adapter; adapter = adapter->Next) { + if (adapter->FirstUnicastAddress) { + SOCKET_ADDRESS *pAddr = &adapter->FirstUnicastAddress->Address; + + /* Ignore address family which we didn't request, just in case */ + if (pAddr->lpSockaddr->sa_family != PJ_AF_INET && + pAddr->lpSockaddr->sa_family != PJ_AF_INET6) + { + continue; + } + + /* Apply some filtering to known IPv4 unusable addresses */ + if (pAddr->lpSockaddr->sa_family == PJ_AF_INET) { + const pj_sockaddr_in *addr_in = + (const pj_sockaddr_in*)pAddr->lpSockaddr; + + /* Ignore 0.0.0.0 address (interface is down?) */ + if (addr_in->sin_addr.s_addr == 0) + continue; + + /* Ignore 0.0.0.0/8 address. This is a special address + * which doesn't seem to have practical use. + */ + if ((pj_ntohl(addr_in->sin_addr.s_addr) >> 24) == 0) + continue; + } + +#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF + /* Ignore loopback interfaces */ + /* This should have been IF_TYPE_SOFTWARE_LOOPBACK according to + * MSDN, and this macro should have been declared in Ipifcons.h, + * but some SDK versions don't have it. + */ + if (adapter->IfType == MIB_IF_TYPE_LOOPBACK) + continue; +#endif + + /* Ignore down interface */ + if (adapter->OperStatus != IfOperStatusUp) + continue; + + ifs[i].addr.sa_family = pAddr->lpSockaddr->sa_family; + pj_memcpy(&ifs[i], pAddr->lpSockaddr, pAddr->iSockaddrLength); + ++i; + } + } + + if (adapterBuf) + free(adapterBuf); + + *p_cnt = i; + return (*p_cnt) ? PJ_SUCCESS : PJ_ENOTFOUND; +} + + +/* + * Enumerate the local IP interface currently active in the host. + */ +PJ_DEF(pj_status_t) pj_enum_ip_interface(int af, + unsigned *p_cnt, + pj_sockaddr ifs[]) +{ + pj_status_t status = -1; + + PJ_ASSERT_RETURN(p_cnt && ifs, PJ_EINVAL); + PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC || af==PJ_AF_INET || af==PJ_AF_INET6, + PJ_EAFNOTSUP); + + status = enum_ipv4_ipv6_interface(af, p_cnt, ifs); + if (status != PJ_SUCCESS && (af==PJ_AF_INET || af==PJ_AF_UNSPEC)) + status = enum_ipv4_interface(p_cnt, ifs); + return status; +} + +/* + * Enumerate the IP routing table for this host. + */ +PJ_DEF(pj_status_t) pj_enum_ip_route(unsigned *p_cnt, + pj_ip_route_entry routes[]) +{ + char ipTabBuff[1024]; + MIB_IPADDRTABLE *pIpTab; + char rtabBuff[1024]; + MIB_IPFORWARDTABLE *prTab; + ULONG tabSize; + unsigned i, count; + DWORD rc = NO_ERROR; + + PJ_ASSERT_RETURN(p_cnt && routes, PJ_EINVAL); + + pIpTab = (MIB_IPADDRTABLE *)ipTabBuff; + prTab = (MIB_IPFORWARDTABLE *)rtabBuff; + + /* First get IP address table */ + tabSize = sizeof(ipTabBuff); + rc = MyGetIpAddrTable(pIpTab, &tabSize, FALSE); + if (rc != NO_ERROR) + return PJ_RETURN_OS_ERROR(rc); + + /* Next get IP route table */ + tabSize = sizeof(rtabBuff); + + rc = MyGetIpForwardTable(prTab, &tabSize, 1); + if (rc != NO_ERROR) + return PJ_RETURN_OS_ERROR(rc); + + /* Reset routes */ + pj_bzero(routes, sizeof(routes[0]) * (*p_cnt)); + + /* Now fill out the route entries */ + count = (prTab->dwNumEntries < *p_cnt) ? prTab->dwNumEntries : *p_cnt; + *p_cnt = 0; + for (i=0; idwNumEntries; ++j) { + if (pIpTab->table[j].dwIndex == prTab->table[i].dwForwardIfIndex) + break; + } + + if (j==pIpTab->dwNumEntries) + continue; /* Interface not found */ + + routes[*p_cnt].ipv4.if_addr.s_addr = pIpTab->table[j].dwAddr; + routes[*p_cnt].ipv4.dst_addr.s_addr = prTab->table[i].dwForwardDest; + routes[*p_cnt].ipv4.mask.s_addr = prTab->table[i].dwForwardMask; + + (*p_cnt)++; + } + + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/list.c b/pjlib/src/pj/list.c new file mode 100644 index 0000000..6ff8d20 --- /dev/null +++ b/pjlib/src/pj/list.c @@ -0,0 +1,26 @@ +/* $Id: list.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + +#if !PJ_FUNCTIONS_ARE_INLINED +# include +#endif + + diff --git a/pjlib/src/pj/lock.c b/pjlib/src/pj/lock.c new file mode 100644 index 0000000..a7879af --- /dev/null +++ b/pjlib/src/pj/lock.c @@ -0,0 +1,198 @@ +/* $Id: lock.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include + + +typedef void LOCK_OBJ; + +/* + * Lock structure. + */ +struct pj_lock_t +{ + LOCK_OBJ *lock_object; + + pj_status_t (*acquire) (LOCK_OBJ*); + pj_status_t (*tryacquire) (LOCK_OBJ*); + pj_status_t (*release) (LOCK_OBJ*); + pj_status_t (*destroy) (LOCK_OBJ*); +}; + +typedef pj_status_t (*FPTR)(LOCK_OBJ*); + +/****************************************************************************** + * Implementation of lock object with mutex. + */ +static pj_lock_t mutex_lock_template = +{ + NULL, + (FPTR) &pj_mutex_lock, + (FPTR) &pj_mutex_trylock, + (FPTR) &pj_mutex_unlock, + (FPTR) &pj_mutex_destroy +}; + +static pj_status_t create_mutex_lock( pj_pool_t *pool, + const char *name, + int type, + pj_lock_t **lock ) +{ + pj_lock_t *p_lock; + pj_mutex_t *mutex; + pj_status_t rc; + + PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL); + + p_lock = PJ_POOL_ALLOC_T(pool, pj_lock_t); + if (!p_lock) + return PJ_ENOMEM; + + pj_memcpy(p_lock, &mutex_lock_template, sizeof(pj_lock_t)); + rc = pj_mutex_create(pool, name, type, &mutex); + if (rc != PJ_SUCCESS) + return rc; + + p_lock->lock_object = mutex; + *lock = p_lock; + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool, + const char *name, + pj_lock_t **lock ) +{ + return create_mutex_lock(pool, name, PJ_MUTEX_SIMPLE, lock); +} + +PJ_DEF(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool, + const char *name, + pj_lock_t **lock ) +{ + return create_mutex_lock(pool, name, PJ_MUTEX_RECURSE, lock); +} + + +/****************************************************************************** + * Implementation of NULL lock object. + */ +static pj_status_t null_op(void *arg) +{ + PJ_UNUSED_ARG(arg); + return PJ_SUCCESS; +} + +static pj_lock_t null_lock_template = +{ + NULL, + &null_op, + &null_op, + &null_op, + &null_op +}; + +PJ_DEF(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool, + const char *name, + pj_lock_t **lock ) +{ + PJ_UNUSED_ARG(name); + PJ_UNUSED_ARG(pool); + + PJ_ASSERT_RETURN(lock, PJ_EINVAL); + + *lock = &null_lock_template; + return PJ_SUCCESS; +} + + +/****************************************************************************** + * Implementation of semaphore lock object. + */ +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 + +static pj_lock_t sem_lock_template = +{ + NULL, + (FPTR) &pj_sem_wait, + (FPTR) &pj_sem_trywait, + (FPTR) &pj_sem_post, + (FPTR) &pj_sem_destroy +}; + +PJ_DEF(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_lock_t **lock ) +{ + pj_lock_t *p_lock; + pj_sem_t *sem; + pj_status_t rc; + + PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL); + + p_lock = PJ_POOL_ALLOC_T(pool, pj_lock_t); + if (!p_lock) + return PJ_ENOMEM; + + pj_memcpy(p_lock, &sem_lock_template, sizeof(pj_lock_t)); + rc = pj_sem_create( pool, name, initial, max, &sem); + if (rc != PJ_SUCCESS) + return rc; + + p_lock->lock_object = sem; + *lock = p_lock; + + return PJ_SUCCESS; +} + + +#endif /* PJ_HAS_SEMAPHORE */ + + +PJ_DEF(pj_status_t) pj_lock_acquire( pj_lock_t *lock ) +{ + PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); + return (*lock->acquire)(lock->lock_object); +} + +PJ_DEF(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock ) +{ + PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); + return (*lock->tryacquire)(lock->lock_object); +} + +PJ_DEF(pj_status_t) pj_lock_release( pj_lock_t *lock ) +{ + PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); + return (*lock->release)(lock->lock_object); +} + +PJ_DEF(pj_status_t) pj_lock_destroy( pj_lock_t *lock ) +{ + PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); + return (*lock->destroy)(lock->lock_object); +} + diff --git a/pjlib/src/pj/log.c b/pjlib/src/pj/log.c new file mode 100644 index 0000000..04173eb --- /dev/null +++ b/pjlib/src/pj/log.c @@ -0,0 +1,543 @@ +/* $Id: log.c 3752 2011-09-18 14:38:46Z bennylp $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include + +#if PJ_LOG_MAX_LEVEL >= 1 + +#if 0 +PJ_DEF_DATA(int) pj_log_max_level = PJ_LOG_MAX_LEVEL; +#else +static int pj_log_max_level = PJ_LOG_MAX_LEVEL; +#endif + +static void *g_last_thread; + +#if PJ_HAS_THREADS +static long thread_suspended_tls_id = -1; +# if PJ_LOG_ENABLE_INDENT +static long thread_indent_tls_id = -1; +# endif +#endif + +#if !PJ_LOG_ENABLE_INDENT || !PJ_HAS_THREADS +static int log_indent; +#endif + +static pj_log_func *log_writer = &pj_log_write; +static unsigned log_decor = PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC | + PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE | + PJ_LOG_HAS_SPACE | PJ_LOG_HAS_THREAD_SWC | + PJ_LOG_HAS_INDENT +#if defined(PJ_WIN32) && PJ_WIN32!=0 + | PJ_LOG_HAS_COLOR +#endif + ; + +static pj_color_t PJ_LOG_COLOR_0 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R; +static pj_color_t PJ_LOG_COLOR_1 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R; +static pj_color_t PJ_LOG_COLOR_2 = PJ_TERM_COLOR_BRIGHT | + PJ_TERM_COLOR_R | + PJ_TERM_COLOR_G; +static pj_color_t PJ_LOG_COLOR_3 = PJ_TERM_COLOR_BRIGHT | + PJ_TERM_COLOR_R | + PJ_TERM_COLOR_G | + PJ_TERM_COLOR_B; +static pj_color_t PJ_LOG_COLOR_4 = PJ_TERM_COLOR_R | + PJ_TERM_COLOR_G | + PJ_TERM_COLOR_B; +static pj_color_t PJ_LOG_COLOR_5 = PJ_TERM_COLOR_R | + PJ_TERM_COLOR_G | + PJ_TERM_COLOR_B; +static pj_color_t PJ_LOG_COLOR_6 = PJ_TERM_COLOR_R | + PJ_TERM_COLOR_G | + PJ_TERM_COLOR_B; +/* Default terminal color */ +static pj_color_t PJ_LOG_COLOR_77 = PJ_TERM_COLOR_R | + PJ_TERM_COLOR_G | + PJ_TERM_COLOR_B; + +#if PJ_LOG_USE_STACK_BUFFER==0 +static char log_buffer[PJ_LOG_MAX_SIZE]; +#endif + +#define LOG_MAX_INDENT 80 + +#if PJ_HAS_THREADS +static void logging_shutdown(void) +{ + if (thread_suspended_tls_id != -1) { + pj_thread_local_free(thread_suspended_tls_id); + thread_suspended_tls_id = -1; + } +# if PJ_LOG_ENABLE_INDENT + if (thread_indent_tls_id != -1) { + pj_thread_local_free(thread_indent_tls_id); + thread_indent_tls_id = -1; + } +# endif +} +#endif /* PJ_HAS_THREADS */ + +#if PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS +static void log_set_indent(int indent) +{ + if (indent < 0) indent = 0; + pj_thread_local_set(thread_indent_tls_id, (void*)(long)indent); +} + +static int log_get_raw_indent() +{ + return (long)pj_thread_local_get(thread_indent_tls_id); +} + +#else +static void log_set_indent(int indent) +{ + log_indent = indent; + if (log_indent < 0) log_indent = 0; +} + +static int log_get_raw_indent() +{ + return log_indent; +} +#endif /* PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS */ + +static int log_get_indent() +{ + int indent = log_get_raw_indent(); + return indent > LOG_MAX_INDENT ? LOG_MAX_INDENT : indent; +} + +PJ_DEF(void) pj_log_add_indent(int indent) +{ + log_set_indent(log_get_raw_indent() + indent); +} + +PJ_DEF(void) pj_log_push_indent(void) +{ + pj_log_add_indent(PJ_LOG_INDENT_SIZE); +} + +PJ_DEF(void) pj_log_pop_indent(void) +{ + pj_log_add_indent(-PJ_LOG_INDENT_SIZE); +} + +pj_status_t pj_log_init(void) +{ +#if PJ_HAS_THREADS + if (thread_suspended_tls_id == -1) { + pj_status_t status; + status = pj_thread_local_alloc(&thread_suspended_tls_id); + if (status != PJ_SUCCESS) + return status; + +# if PJ_LOG_ENABLE_INDENT + status = pj_thread_local_alloc(&thread_indent_tls_id); + if (status != PJ_SUCCESS) { + pj_thread_local_free(thread_suspended_tls_id); + thread_suspended_tls_id = -1; + return status; + } +# endif + pj_atexit(&logging_shutdown); + } +#endif + g_last_thread = NULL; + return PJ_SUCCESS; +} + +PJ_DEF(void) pj_log_set_decor(unsigned decor) +{ + log_decor = decor; +} + +PJ_DEF(unsigned) pj_log_get_decor(void) +{ + return log_decor; +} + +PJ_DEF(void) pj_log_set_color(int level, pj_color_t color) +{ + switch (level) + { + case 0: PJ_LOG_COLOR_0 = color; + break; + case 1: PJ_LOG_COLOR_1 = color; + break; + case 2: PJ_LOG_COLOR_2 = color; + break; + case 3: PJ_LOG_COLOR_3 = color; + break; + case 4: PJ_LOG_COLOR_4 = color; + break; + case 5: PJ_LOG_COLOR_5 = color; + break; + case 6: PJ_LOG_COLOR_6 = color; + break; + /* Default terminal color */ + case 77: PJ_LOG_COLOR_77 = color; + break; + default: + /* Do nothing */ + break; + } +} + +PJ_DEF(pj_color_t) pj_log_get_color(int level) +{ + switch (level) { + case 0: + return PJ_LOG_COLOR_0; + case 1: + return PJ_LOG_COLOR_1; + case 2: + return PJ_LOG_COLOR_2; + case 3: + return PJ_LOG_COLOR_3; + case 4: + return PJ_LOG_COLOR_4; + case 5: + return PJ_LOG_COLOR_5; + case 6: + return PJ_LOG_COLOR_6; + default: + /* Return default terminal color */ + return PJ_LOG_COLOR_77; + } +} + +PJ_DEF(void) pj_log_set_level(int level) +{ + pj_log_max_level = level; +} + +#if 1 +PJ_DEF(int) pj_log_get_level(void) +{ + return pj_log_max_level; +} +#endif + +PJ_DEF(void) pj_log_set_log_func( pj_log_func *func ) +{ + log_writer = func; +} + +PJ_DEF(pj_log_func*) pj_log_get_log_func(void) +{ + return log_writer; +} + +/* Temporarily suspend logging facility for this thread. + * If thread local storage/variable is not used or not initialized, then + * we can only suspend the logging globally across all threads. This may + * happen e.g. when log function is called before PJLIB is fully initialized + * or after PJLIB is shutdown. + */ +static void suspend_logging(int *saved_level) +{ + /* Save the level regardless, just in case PJLIB is shutdown + * between suspend and resume. + */ + *saved_level = pj_log_max_level; + +#if PJ_HAS_THREADS + if (thread_suspended_tls_id != -1) + { + pj_thread_local_set(thread_suspended_tls_id, (void*)PJ_TRUE); + } + else +#endif + { + pj_log_max_level = 0; + } +} + +/* Resume logging facility for this thread */ +static void resume_logging(int *saved_level) +{ +#if PJ_HAS_THREADS + if (thread_suspended_tls_id != -1) + { + pj_thread_local_set(thread_suspended_tls_id, (void*)PJ_FALSE); + } + else +#endif + { + /* Only revert the level if application doesn't change the + * logging level between suspend and resume. + */ + if (pj_log_max_level==0 && *saved_level) + pj_log_max_level = *saved_level; + } +} + +/* Is logging facility suspended for this thread? */ +static pj_bool_t is_logging_suspended(void) +{ +#if PJ_HAS_THREADS + if (thread_suspended_tls_id != -1) + { + return pj_thread_local_get(thread_suspended_tls_id) != NULL; + } + else +#endif + { + return pj_log_max_level == 0; + } +} + +PJ_DEF(void) pj_log( const char *sender, int level, + const char *format, va_list marker) +{ + pj_time_val now; + pj_parsed_time ptime; + char *pre; +#if PJ_LOG_USE_STACK_BUFFER + char log_buffer[PJ_LOG_MAX_SIZE]; +#endif + int saved_level, len, print_len, indent; + + PJ_CHECK_STACK(); + + if (level > pj_log_max_level) + return; + + if (is_logging_suspended()) + return; + + /* Temporarily disable logging for this thread. Some of PJLIB APIs that + * this function calls below will recursively call the logging function + * back, hence it will cause infinite recursive calls if we allow that. + */ + suspend_logging(&saved_level); + + /* Get current date/time. */ + pj_gettimeofday(&now); + pj_time_decode(&now, &ptime); + + pre = log_buffer; + if (log_decor & PJ_LOG_HAS_LEVEL_TEXT) { + static const char *ltexts[] = { "FATAL:", "ERROR:", " WARN:", + " INFO:", "DEBUG:", "TRACE:", "DETRC:"}; + pj_ansi_strcpy(pre, ltexts[level]); + pre += 6; + } + if (log_decor & PJ_LOG_HAS_DAY_NAME) { + static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"}; + pj_ansi_strcpy(pre, wdays[ptime.wday]); + pre += 3; + } + if (log_decor & PJ_LOG_HAS_YEAR) { + if (pre!=log_buffer) *pre++ = ' '; + pre += pj_utoa(ptime.year, pre); + } + if (log_decor & PJ_LOG_HAS_MONTH) { + *pre++ = '-'; + pre += pj_utoa_pad(ptime.mon+1, pre, 2, '0'); + } + if (log_decor & PJ_LOG_HAS_DAY_OF_MON) { + *pre++ = '-'; + pre += pj_utoa_pad(ptime.day, pre, 2, '0'); + } + if (log_decor & PJ_LOG_HAS_TIME) { + if (pre!=log_buffer) *pre++ = ' '; + pre += pj_utoa_pad(ptime.hour, pre, 2, '0'); + *pre++ = ':'; + pre += pj_utoa_pad(ptime.min, pre, 2, '0'); + *pre++ = ':'; + pre += pj_utoa_pad(ptime.sec, pre, 2, '0'); + } + if (log_decor & PJ_LOG_HAS_MICRO_SEC) { + *pre++ = '.'; + pre += pj_utoa_pad(ptime.msec, pre, 3, '0'); + } + if (log_decor & PJ_LOG_HAS_SENDER) { + enum { SENDER_WIDTH = 14 }; + int sender_len = strlen(sender); + if (pre!=log_buffer) *pre++ = ' '; + if (sender_len <= SENDER_WIDTH) { + while (sender_len < SENDER_WIDTH) + *pre++ = ' ', ++sender_len; + while (*sender) + *pre++ = *sender++; + } else { + int i; + for (i=0; i 0) { + pj_memset(pre, PJ_LOG_INDENT_CHAR, indent); + pre += indent; + } + } +#endif + + len = pre - log_buffer; + + /* Print the whole message to the string log_buffer. */ + print_len = pj_ansi_vsnprintf(pre, sizeof(log_buffer)-len, format, + marker); + if (print_len < 0) { + level = 1; + print_len = pj_ansi_snprintf(pre, sizeof(log_buffer)-len, + ""); + } + len = len + print_len; + if (len > 0 && len < (int)sizeof(log_buffer)-2) { + if (log_decor & PJ_LOG_HAS_CR) { + log_buffer[len++] = '\r'; + } + if (log_decor & PJ_LOG_HAS_NEWLINE) { + log_buffer[len++] = '\n'; + } + log_buffer[len] = '\0'; + } else { + len = sizeof(log_buffer)-1; + if (log_decor & PJ_LOG_HAS_CR) { + log_buffer[sizeof(log_buffer)-3] = '\r'; + } + if (log_decor & PJ_LOG_HAS_NEWLINE) { + log_buffer[sizeof(log_buffer)-2] = '\n'; + } + log_buffer[sizeof(log_buffer)-1] = '\0'; + } + + /* It should be safe to resume logging at this point. Application can + * recursively call the logging function inside the callback. + */ + resume_logging(&saved_level); + + if (log_writer) + (*log_writer)(level, log_buffer, len); +} + +/* +PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 0, format, arg); + va_end(arg); +} +*/ + +PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 1, format, arg); + va_end(arg); +} +#endif /* PJ_LOG_MAX_LEVEL >= 1 */ + +#if PJ_LOG_MAX_LEVEL >= 2 +PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 2, format, arg); + va_end(arg); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 3 +PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 3, format, arg); + va_end(arg); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 4 +PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 4, format, arg); + va_end(arg); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 5 +PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 5, format, arg); + va_end(arg); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 6 +PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 6, format, arg); + va_end(arg); +} +#endif + diff --git a/pjlib/src/pj/log_writer_printk.c b/pjlib/src/pj/log_writer_printk.c new file mode 100644 index 0000000..c75fb67 --- /dev/null +++ b/pjlib/src/pj/log_writer_printk.c @@ -0,0 +1,28 @@ +/* $Id: log_writer_printk.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include + +PJ_DEF(void) pj_log_write(int level, const char *buffer, int len) +{ + PJ_CHECK_STACK(); + printk(KERN_INFO "%s", buffer); +} + diff --git a/pjlib/src/pj/log_writer_stdout.c b/pjlib/src/pj/log_writer_stdout.c new file mode 100644 index 0000000..b1e5482 --- /dev/null +++ b/pjlib/src/pj/log_writer_stdout.c @@ -0,0 +1,57 @@ +/* $Id: log_writer_stdout.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include + + +static void term_set_color(int level) +{ +#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 + pj_term_set_color(pj_log_get_color(level)); +#else + PJ_UNUSED_ARG(level); +#endif +} + +static void term_restore_color(void) +{ +#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 + /* Set terminal to its default color */ + pj_term_set_color(pj_log_get_color(77)); +#endif +} + + +PJ_DEF(void) pj_log_write(int level, const char *buffer, int len) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(len); + + /* Copy to terminal/file. */ + if (pj_log_get_decor() & PJ_LOG_HAS_COLOR) { + term_set_color(level); + printf("%s", buffer); + term_restore_color(); + } else { + printf("%s", buffer); + } +} + diff --git a/pjlib/src/pj/log_writer_symbian_console.cpp b/pjlib/src/pj/log_writer_symbian_console.cpp new file mode 100644 index 0000000..525394e --- /dev/null +++ b/pjlib/src/pj/log_writer_symbian_console.cpp @@ -0,0 +1,44 @@ +/* $Id: log_writer_symbian_console.cpp 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include + +#include "os_symbian.h" +#include + +PJ_DEF(void) pj_log_write(int level, const char *buffer, int len) +{ +#if 0 + wchar_t wbuffer[PJ_LOG_MAX_SIZE]; + CConsoleBase *cons = PjSymbianOS::Instance->Console(); + + pj_ansi_to_unicode(buffer, len, wbuffer, PJ_ARRAY_SIZE(wbuffer)); + + + TPtrC16 aPtr((TUint16*)wbuffer, len); + console->Write(aPtr); +#else + PJ_UNUSED_ARG(level); + PJ_UNUSED_ARG(buffer); + PJ_UNUSED_ARG(len); +#endif +} + diff --git a/pjlib/src/pj/os_core_darwin.m b/pjlib/src/pj/os_core_darwin.m new file mode 100644 index 0000000..f93f425 --- /dev/null +++ b/pjlib/src/pj/os_core_darwin.m @@ -0,0 +1,100 @@ +/* $Id: os_core_darwin.m 3670 2011-07-20 03:00:48Z ming $ */ +/* + * Copyright (C) 2011-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 +#include "TargetConditionals.h" + +#if TARGET_OS_IPHONE + +PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], + unsigned flags) +{ + return (*main_func)(argc, argv); +} + +#else + +#include +#include +#include +#include + +#define THIS_FILE "os_core_darwin.m" + +typedef struct run_app_t { + pj_main_func_ptr main_func; + int argc; + char **argv; + int retval; +} run_app_t; + +@interface DeadThread: NSObject { ;; } ++ (void)enterMultiThreadedMode; ++ (void)emptyThreadMethod:(id)obj; +@end + +@implementation DeadThread ++ (void)enterMultiThreadedMode +{ + [NSThread detachNewThreadSelector:@selector(emptyThreadMethod:) + toTarget:[DeadThread class] withObject:nil]; +} + ++ (void)emptyThreadMethod:(id)obj { ; } +@end + +static void* main_thread(void *data) +{ + run_app_t *param = (run_app_t *)data; + + param->retval = (*param->main_func)(param->argc, param->argv); + CFRunLoopStop(CFRunLoopGetMain()); + + return NULL; +} + +/* + * pj_run_app() + * This function has to be called from the main thread. The purpose of + * this function is to initialize the application's memory pool, event + * loop management, and multi-threading environment. + */ +PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], + unsigned flags) +{ + pthread_t thread; + run_app_t param; + NSAutoreleasePool *pool; + + pool = [[NSAutoreleasePool alloc] init]; + [NSApplication sharedApplication]; + [DeadThread enterMultiThreadedMode]; + + param.argc = argc; + param.argv = (char **)argv; + param.main_func = main_func; + if (pthread_create(&thread, NULL, &main_thread, ¶m) == 0) { + CFRunLoopRun(); + } + + PJ_UNUSED_ARG(pool); + + return param.retval; +} + +#endif diff --git a/pjlib/src/pj/os_core_linux_kernel.c b/pjlib/src/pj/os_core_linux_kernel.c new file mode 100644 index 0000000..c1865ef --- /dev/null +++ b/pjlib/src/pj/os_core_linux_kernel.c @@ -0,0 +1,698 @@ +/* $Id: os_core_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#if defined(MODVERSIONS) +#include +#endif +#include +#include +//#include +#include +#include + +#include +#include +#include + +#define THIS_FILE "oslinuxkern" + +struct pj_thread_t +{ + /** Thread's name. */ + char obj_name[PJ_MAX_OBJ_NAME]; + + /** Linux task structure for thread. */ + struct task_struct *thread; + + /** Flags (specified in pj_thread_create) */ + unsigned flags; + + /** Task queue needed to launch thread. */ + //struct tq_struct tq; + + /** Semaphore needed to control thread startup. */ + struct semaphore startstop_sem; + + /** Semaphore to suspend thread during startup. */ + struct semaphore suspend_sem; + + /** Queue thread is waiting on. Gets initialized by + thread_initialize, can be used by thread itself. + */ + wait_queue_head_t queue; + + /** Flag to tell thread whether to die or not. + When the thread receives a signal, it must check + the value of terminate and call thread_deinitialize and terminate + if set. + */ + int terminate; + + /** Thread's entry. */ + pj_thread_proc *func; + + /** Argument. */ + void *arg; +}; + +struct pj_atomic_t +{ + atomic_t atom; +}; + +struct pj_mutex_t +{ + struct semaphore sem; + pj_bool_t recursive; + pj_thread_t *owner; + int own_count; +}; + +struct pj_sem_t +{ + struct semaphore sem; +}; + +/* + * Static global variables. + */ +#define MAX_TLS_ID 32 +static void *tls_values[MAX_TLS_ID]; +static int tls_id; +static long thread_tls_id; +static spinlock_t critical_section = SPIN_LOCK_UNLOCKED; +static unsigned long spinlock_flags; +static pj_thread_t main_thread; + +/* private functions */ +//#define TRACE_(expr) PJ_LOG(3,expr) +#define TRACE_(x) + + +/* This must be called in the context of the new thread. */ +static void thread_initialize( pj_thread_t *thread ) +{ + TRACE_((THIS_FILE, "---new thread initializing...")); + + /* Set TLS */ + pj_thread_local_set(thread_tls_id, thread); + + /* fill in thread structure */ + thread->thread = current; + pj_assert(thread->thread != NULL); + + /* set signal mask to what we want to respond */ + siginitsetinv(¤t->blocked, + sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)); + + /* initialise wait queue */ + init_waitqueue_head(&thread->queue); + + /* initialise termination flag */ + thread->terminate = 0; + + /* set name of this process (making sure obj_name is null + * terminated first) + */ + thread->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + sprintf(current->comm, thread->obj_name); + + /* tell the creator that we are ready and let him continue */ + up(&thread->startstop_sem); +} + +/* cleanup of thread. Called by the exiting thread. */ +static void thread_deinitialize(pj_thread_t *thread) +{ + /* we are terminating */ + + /* lock the kernel, the exit will unlock it */ + thread->thread = NULL; + mb(); + + /* notify the stop_kthread() routine that we are terminating. */ + up(&thread->startstop_sem); + + /* the kernel_thread that called clone() does a do_exit here. */ + + /* there is no race here between execution of the "killer" and + real termination of the thread (race window between up and do_exit), + since both the thread and the "killer" function are running with + the kernel lock held. + The kernel lock will be freed after the thread exited, so the code + is really not executed anymore as soon as the unload functions gets + the kernel lock back. + The init process may not have made the cleanup of the process here, + but the cleanup can be done safely with the module unloaded. + */ + +} + +static int thread_proc(void *arg) +{ + pj_thread_t *thread = arg; + + TRACE_((THIS_FILE, "---new thread starting!")); + + /* Initialize thread. */ + thread_initialize( thread ); + + /* Wait if created suspended. */ + if (thread->flags & PJ_THREAD_SUSPENDED) { + TRACE_((THIS_FILE, "---new thread suspended...")); + down(&thread->suspend_sem); + } + + TRACE_((THIS_FILE, "---new thread running...")); + + pj_assert(thread->func != NULL); + + /* Call thread's entry. */ + (*thread->func)(thread->arg); + + TRACE_((THIS_FILE, "---thread exiting...")); + + /* Cleanup thread. */ + thread_deinitialize(thread); + + return 0; +} + +/* The very task entry. */ +static void kthread_launcher(void *arg) +{ + TRACE_((THIS_FILE, "...launching thread!...")); + kernel_thread(&thread_proc, arg, 0); +} + +PJ_DEF(pj_status_t) pj_init(void) +{ + pj_status_t rc; + + PJ_LOG(5, ("pj_init", "Initializing PJ Library..")); + + rc = pj_thread_init(); + if (rc != PJ_SUCCESS) + return rc; + + /* Initialize exception ID for the pool. + * Must do so after critical section is configured. + */ + rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); + if (rc != PJ_SUCCESS) + return rc; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_uint32_t) pj_getpid(void) +{ + return 1; +} + +PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, + pj_thread_desc desc, + pj_thread_t **ptr_thread) +{ + char stack_ptr; + pj_thread_t *thread = (pj_thread_t *)desc; + pj_str_t thread_name = pj_str((char*)cstr_thread_name); + + /* Size sanity check. */ + if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { + pj_assert(!"Not enough pj_thread_desc size!"); + return PJ_EBUG; + } + + /* If a thread descriptor has been registered before, just return it. */ + if (pj_thread_local_get (thread_tls_id) != 0) { + // 2006-02-26 bennylp: + // This wouldn't work in all cases!. + // If thread is created by external module (e.g. sound thread), + // thread may be reused while the pool used for the thread descriptor + // has been deleted by application. + //*thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id); + //return PJ_SUCCESS; + } + + /* Initialize and set the thread entry. */ + pj_bzero(desc, sizeof(struct pj_thread_t)); + + if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) + pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread); + else + pj_snprintf(thread->obj_name, sizeof(thread->obj_name), + "thr%p", (void*)thread->thread); + + /* Initialize. */ + thread_initialize(thread); + + /* Eat semaphore. */ + down(&thread->startstop_sem); + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + thread->stk_start = &stack_ptr; + thread->stk_size = 0xFFFFFFFFUL; + thread->stk_max_usage = 0; +#else + stack_ptr = '\0'; +#endif + + *ptr_thread = thread; + return PJ_SUCCESS; +} + + +pj_status_t pj_thread_init(void) +{ + pj_status_t rc; + pj_thread_t *dummy; + + rc = pj_thread_local_alloc(&thread_tls_id); + if (rc != PJ_SUCCESS) + return rc; + + return pj_thread_register("pjlib-main", (long*)&main_thread, &dummy); +} + +PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name, + pj_thread_proc *proc, void *arg, + pj_size_t stack_size, unsigned flags, + pj_thread_t **ptr_thread) +{ + pj_thread_t *thread; + + TRACE_((THIS_FILE, "pj_thread_create()")); + + PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL); + + thread = pj_pool_zalloc(pool, sizeof(pj_thread_t)); + if (!thread) + return PJ_ENOMEM; + + PJ_UNUSED_ARG(stack_size); + + /* Thread name. */ + if (!thread_name) + thread_name = "thr%p"; + + if (strchr(thread_name, '%')) { + pj_snprintf(thread->obj_name, PJ_MAX_OBJ_NAME, thread_name, thread); + } else { + strncpy(thread->obj_name, thread_name, PJ_MAX_OBJ_NAME); + thread->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + /* Init thread's semaphore. */ + TRACE_((THIS_FILE, "...init semaphores...")); + init_MUTEX_LOCKED(&thread->startstop_sem); + init_MUTEX_LOCKED(&thread->suspend_sem); + + thread->flags = flags; + + if ((flags & PJ_THREAD_SUSPENDED) == 0) { + up(&thread->suspend_sem); + } + + /* Store the functions and argument. */ + thread->func = proc; + thread->arg = arg; + + /* Save return value. */ + *ptr_thread = thread; + + /* Create the new thread by running a task through keventd. */ + +#if 0 + /* Initialize the task queue struct. */ + thread->tq.sync = 0; + INIT_LIST_HEAD(&thread->tq.list); + thread->tq.routine = kthread_launcher; + thread->tq.data = thread; + + /* and schedule it for execution. */ + schedule_task(&thread->tq); +#endif + kthread_launcher(thread); + + /* Wait until thread has reached the setup_thread routine. */ + TRACE_((THIS_FILE, "...wait for the new thread...")); + down(&thread->startstop_sem); + + TRACE_((THIS_FILE, "...main thread resumed...")); + return PJ_SUCCESS; +} + +PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *thread) +{ + return thread->obj_name; +} + +PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *thread) +{ + up(&thread->suspend_sem); + return PJ_SUCCESS; +} + +PJ_DEF(pj_thread_t*) pj_thread_this(void) +{ + return (pj_thread_t*)pj_thread_local_get(thread_tls_id); +} + +PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) +{ + TRACE_((THIS_FILE, "pj_thread_join()")); + down(&p->startstop_sem); + TRACE_((THIS_FILE, " joined!")); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *thread) +{ + PJ_ASSERT_RETURN(thread != NULL, PJ_EINVALIDOP); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) +{ + pj_highprec_t ticks; + pj_thread_t *thread = pj_thread_this(); + + PJ_ASSERT_RETURN(thread != NULL, PJ_EBUG); + + /* Use high precision calculation to make sure we don't + * crop values: + * + * ticks = HZ * msec / 1000 + */ + ticks = HZ; + pj_highprec_mul(ticks, msec); + pj_highprec_div(ticks, 1000); + + TRACE_((THIS_FILE, "this thread will sleep for %u ticks", ticks)); + interruptible_sleep_on_timeout( &thread->queue, ticks); + return PJ_SUCCESS; +} + + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, + pj_atomic_value_t value, + pj_atomic_t **ptr_var) +{ + pj_atomic_t *t = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t)); + if (!t) return PJ_ENOMEM; + + atomic_set(&t->atom, value); + *ptr_var = t; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var ) +{ + return PJ_SUCCESS; +} + +PJ_DEF(void) pj_atomic_set(pj_atomic_t *var, pj_atomic_value_t value) +{ + atomic_set(&var->atom, value); +} + +PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *var) +{ + return atomic_read(&var->atom); +} + +PJ_DEF(void) pj_atomic_inc(pj_atomic_t *var) +{ + atomic_inc(&var->atom); +} + +PJ_DEF(void) pj_atomic_dec(pj_atomic_t *var) +{ + atomic_dec(&var->atom); +} + +PJ_DEF(void) pj_atomic_add( pj_atomic_t *var, pj_atomic_value_t value ) +{ + atomic_add(value, &var->atom); +} + + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index) +{ + if (tls_id >= MAX_TLS_ID) + return PJ_ETOOMANY; + + *index = tls_id++; + + return PJ_SUCCESS; +} + +PJ_DEF(void) pj_thread_local_free(long index) +{ + pj_assert(index >= 0 && index < MAX_TLS_ID); +} + +PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value) +{ + pj_assert(index >= 0 && index < MAX_TLS_ID); + tls_values[index] = value; + return PJ_SUCCESS; +} + +PJ_DEF(void*) pj_thread_local_get(long index) +{ + pj_assert(index >= 0 && index < MAX_TLS_ID); + return tls_values[index]; +} + + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(void) pj_enter_critical_section(void) +{ + spin_lock_irqsave(&critical_section, spinlock_flags); +} + +PJ_DEF(void) pj_leave_critical_section(void) +{ + spin_unlock_irqrestore(&critical_section, spinlock_flags); +} + + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(pj_status_t) pj_mutex_create( pj_pool_t *pool, + const char *name, + int type, + pj_mutex_t **ptr_mutex) +{ + pj_mutex_t *mutex; + + PJ_UNUSED_ARG(name); + + mutex = pj_pool_alloc(pool, sizeof(pj_mutex_t)); + if (!mutex) + return PJ_ENOMEM; + + init_MUTEX(&mutex->sem); + + mutex->recursive = (type == PJ_MUTEX_RECURSE); + mutex->owner = NULL; + mutex->own_count = 0; + + /* Done. */ + *ptr_mutex = mutex; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); +} + +PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create( pool, name, PJ_MUTEX_RECURSE, mutex); +} + +PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) +{ + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + if (mutex->recursive) { + pj_thread_t *this_thread = pj_thread_this(); + if (mutex->owner == this_thread) { + ++mutex->own_count; + } else { + down(&mutex->sem); + pj_assert(mutex->own_count == 0); + mutex->owner = this_thread; + mutex->own_count = 1; + } + } else { + down(&mutex->sem); + } + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) +{ + long rc; + + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + if (mutex->recursive) { + pj_thread_t *this_thread = pj_thread_this(); + if (mutex->owner == this_thread) { + ++mutex->own_count; + } else { + rc = down_interruptible(&mutex->sem); + if (rc != 0) + return PJ_RETURN_OS_ERROR(-rc); + pj_assert(mutex->own_count == 0); + mutex->owner = this_thread; + mutex->own_count = 1; + } + } else { + int rc = down_trylock(&mutex->sem); + if (rc != 0) + return PJ_RETURN_OS_ERROR(-rc); + } + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) +{ + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + if (mutex->recursive) { + pj_thread_t *this_thread = pj_thread_this(); + if (mutex->owner == this_thread) { + pj_assert(mutex->own_count > 0); + --mutex->own_count; + if (mutex->own_count == 0) { + mutex->owner = NULL; + up(&mutex->sem); + } + } else { + pj_assert(!"Not owner!"); + return PJ_EINVALIDOP; + } + } else { + up(&mutex->sem); + } + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) +{ + PJ_ASSERT_RETURN(mutex != NULL, PJ_EINVAL); + + return PJ_SUCCESS; +} + +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 +PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) +{ + if (mutex->recursive) + return mutex->owner == pj_thread_this(); + else + return 1; +} +#endif /* PJ_DEBUG */ + + +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 + +PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_sem_t **sem) +{ + pj_sem_t *sem; + + PJ_UNUSED_ARG(max); + + PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); + + sem = pj_pool_alloc(pool, sizeof(pj_sem_t)); + sema_init(&sem->sem, initial); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) +{ + PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); + + down(&sem->sem); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) +{ + int rc; + + PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); + + rc = down_trylock(&sem->sem); + if (rc != 0) { + return PJ_RETURN_OS_ERROR(-rc); + } else { + return PJ_SUCCESS; + } +} + +PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) +{ + PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); + + up(&sem->sem); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) +{ + PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); + + return PJ_SUCCESS; +} + +#endif /* PJ_HAS_SEMAPHORE */ + + + + diff --git a/pjlib/src/pj/os_core_symbian.cpp b/pjlib/src/pj/os_core_symbian.cpp new file mode 100644 index 0000000..f9f5d7d --- /dev/null +++ b/pjlib/src/pj/os_core_symbian.cpp @@ -0,0 +1,1063 @@ +/* $Id: os_core_symbian.cpp 3999 2012-03-30 07:10:13Z bennylp $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "os_symbian.h" + + +#define PJ_MAX_TLS 32 +#define DUMMY_MUTEX ((pj_mutex_t*)101) +#define DUMMY_SEMAPHORE ((pj_sem_t*)102) +#define THIS_FILE "os_core_symbian.c" + +/* Default message slot number for RSocketServ::Connect(). + * Increase it to 32 from the default 8 (KESockDefaultMessageSlots) + */ +#ifndef PJ_SYMBIAN_SOCK_MSG_SLOTS +# define PJ_SYMBIAN_SOCK_MSG_SLOTS 32 +#endif + +/* + * Note: + * + * The Symbian implementation does not support threading! + */ + +struct pj_thread_t +{ + char obj_name[PJ_MAX_OBJ_NAME]; + void *tls_values[PJ_MAX_TLS]; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + pj_uint32_t stk_size; + pj_uint32_t stk_max_usage; + char *stk_start; + const char *caller_file; + int caller_line; +#endif + +} main_thread; + +struct pj_atomic_t +{ + pj_atomic_value_t value; +}; + +struct pj_sem_t +{ + int value; + int max; +}; + +/* Flag and reference counter for PJLIB instance */ +static int initialized; + +/* Flags to indicate which TLS variables have been used */ +static int tls_vars[PJ_MAX_TLS]; + +/* atexit handlers */ +static unsigned atexit_count; +static void (*atexit_func[32])(void); + + +///////////////////////////////////////////////////////////////////////////// +// +// CPjTimeoutTimer implementation +// + +CPjTimeoutTimer::CPjTimeoutTimer() +: CActive(PJ_SYMBIAN_TIMER_PRIORITY), hasTimedOut_(PJ_FALSE) +{ +} + +CPjTimeoutTimer::~CPjTimeoutTimer() +{ + Cancel(); + timer_.Close(); +} + +void CPjTimeoutTimer::ConstructL() +{ + hasTimedOut_ = PJ_FALSE; + timer_.CreateLocal(); + CActiveScheduler::Add(this); +} + +CPjTimeoutTimer *CPjTimeoutTimer::NewL() +{ + CPjTimeoutTimer *self = new CPjTimeoutTimer; + CleanupStack::PushL(self); + + self->ConstructL(); + + CleanupStack::Pop(self); + return self; + +} + +void CPjTimeoutTimer::StartTimer(TUint miliSeconds) +{ + Cancel(); + + hasTimedOut_ = PJ_FALSE; + timer_.After(iStatus, miliSeconds * 1000); + SetActive(); +} + +bool CPjTimeoutTimer::HasTimedOut() const +{ + return hasTimedOut_ != 0; +} + +void CPjTimeoutTimer::RunL() +{ + hasTimedOut_ = PJ_TRUE; +} + +void CPjTimeoutTimer::DoCancel() +{ + timer_.Cancel(); +} + +TInt CPjTimeoutTimer::RunError(TInt aError) +{ + PJ_UNUSED_ARG(aError); + return KErrNone; +} + + + +///////////////////////////////////////////////////////////////////////////// +// +// PjSymbianOS implementation +// + +PjSymbianOS::PjSymbianOS() +: isConnectionUp_(false), + isSocketServInitialized_(false), isResolverInitialized_(false), + console_(NULL), selectTimeoutTimer_(NULL), + appSocketServ_(NULL), appConnection_(NULL), appHostResolver_(NULL), + appHostResolver6_(NULL) +{ +} + +// Set parameters +void PjSymbianOS::SetParameters(pj_symbianos_params *params) +{ + appSocketServ_ = (RSocketServ*) params->rsocketserv; + appConnection_ = (RConnection*) params->rconnection; + appHostResolver_ = (RHostResolver*) params->rhostresolver; + appHostResolver6_ = (RHostResolver*) params->rhostresolver6; +} + +// Get PjSymbianOS instance +PjSymbianOS *PjSymbianOS::Instance() +{ + static PjSymbianOS instance_; + return &instance_; +} + + +// Initialize +TInt PjSymbianOS::Initialize() +{ + TInt err; + + selectTimeoutTimer_ = CPjTimeoutTimer::NewL(); + +#if 0 + pj_assert(console_ == NULL); + TRAPD(err, console_ = Console::NewL(_L("PJLIB"), + TSize(KConsFullScreen,KConsFullScreen))); + return err; +#endif + + /* Only create RSocketServ if application doesn't specify it + * in the parameters + */ + if (!isSocketServInitialized_ && appSocketServ_ == NULL) { + err = socketServ_.Connect(PJ_SYMBIAN_SOCK_MSG_SLOTS); + if (err != KErrNone) + goto on_error; + + isSocketServInitialized_ = true; + } + + if (!isResolverInitialized_) { + if (appHostResolver_ == NULL) { + if (Connection()) + err = hostResolver_.Open(SocketServ(), KAfInet, KSockStream, + *Connection()); + else + err = hostResolver_.Open(SocketServ(), KAfInet, KSockStream); + + if (err != KErrNone) + goto on_error; + } + +#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0 + if (appHostResolver6_ == NULL) { + if (Connection()) + err = hostResolver6_.Open(SocketServ(), KAfInet6, KSockStream, + *Connection()); + else + err = hostResolver6_.Open(SocketServ(), KAfInet6, KSockStream); + + if (err != KErrNone) + goto on_error; + } +#endif + + + isResolverInitialized_ = true; + } + + isConnectionUp_ = true; + + return KErrNone; + +on_error: + Shutdown(); + return err; +} + +// Shutdown +void PjSymbianOS::Shutdown() +{ + isConnectionUp_ = false; + + if (isResolverInitialized_) { + hostResolver_.Close(); +#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0 + hostResolver6_.Close(); +#endif + isResolverInitialized_ = false; + } + + if (isSocketServInitialized_) { + socketServ_.Close(); + isSocketServInitialized_ = false; + } + + delete console_; + console_ = NULL; + + delete selectTimeoutTimer_; + selectTimeoutTimer_ = NULL; + + appSocketServ_ = NULL; + appConnection_ = NULL; + appHostResolver_ = NULL; + appHostResolver6_ = NULL; +} + +// Convert to Unicode +TInt PjSymbianOS::ConvertToUnicode(TDes16 &aUnicode, const TDesC8 &aForeign) +{ +#if 0 + pj_assert(conv_ != NULL); + return conv_->ConvertToUnicode(aUnicode, aForeign, convToUnicodeState_); +#else + return CnvUtfConverter::ConvertToUnicodeFromUtf8(aUnicode, aForeign); +#endif +} + +// Convert from Unicode +TInt PjSymbianOS::ConvertFromUnicode(TDes8 &aForeign, const TDesC16 &aUnicode) +{ +#if 0 + pj_assert(conv_ != NULL); + return conv_->ConvertFromUnicode(aForeign, aUnicode, convToAnsiState_); +#else + return CnvUtfConverter::ConvertFromUnicodeToUtf8(aForeign, aUnicode); +#endif +} + + +///////////////////////////////////////////////////////////////////////////// +// +// PJLIB os.h implementation +// + +PJ_DEF(pj_uint32_t) pj_getpid(void) +{ + return 0; +} + + +/* Set Symbian specific parameters */ +PJ_DEF(pj_status_t) pj_symbianos_set_params(pj_symbianos_params *prm) +{ + PJ_ASSERT_RETURN(prm != NULL, PJ_EINVAL); + PjSymbianOS::Instance()->SetParameters(prm); + return PJ_SUCCESS; +} + + +/* Set connection status */ +PJ_DEF(void) pj_symbianos_set_connection_status(pj_bool_t up) +{ + PjSymbianOS::Instance()->SetConnectionStatus(up != 0); +} + + +/* + * pj_init(void). + * Init PJLIB! + */ +PJ_DEF(pj_status_t) pj_init(void) +{ + char stack_ptr; + pj_status_t status; + + /* Check if PJLIB have been initialized */ + if (initialized) { + ++initialized; + return PJ_SUCCESS; + } + + pj_ansi_strcpy(main_thread.obj_name, "pjthread"); + + // Init main thread + pj_memset(&main_thread, 0, sizeof(main_thread)); + + // Initialize PjSymbianOS instance + PjSymbianOS *os = PjSymbianOS::Instance(); + + PJ_LOG(4,(THIS_FILE, "Initializing PJLIB for Symbian OS..")); + + TInt err; + err = os->Initialize(); + if (err != KErrNone) + return PJ_RETURN_OS_ERROR(err); + + /* Init logging */ + pj_log_init(); + + /* Initialize exception ID for the pool. + * Must do so after critical section is configured. + */ + status = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); + if (status != PJ_SUCCESS) + goto on_error; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + main_thread.stk_start = &stack_ptr; + main_thread.stk_size = 0xFFFFFFFFUL; + main_thread.stk_max_usage = 0; +#else + stack_ptr = '\0'; +#endif + + /* Flag PJLIB as initialized */ + ++initialized; + pj_assert(initialized == 1); + + PJ_LOG(5,(THIS_FILE, "PJLIB initialized.")); + return PJ_SUCCESS; + +on_error: + pj_shutdown(); + return PJ_RETURN_OS_ERROR(err); +} + + +PJ_DEF(pj_status_t) pj_atexit(pj_exit_callback func) +{ + if (atexit_count >= PJ_ARRAY_SIZE(atexit_func)) + return PJ_ETOOMANY; + + atexit_func[atexit_count++] = func; + return PJ_SUCCESS; +} + + + +PJ_DEF(void) pj_shutdown(void) +{ + /* Only perform shutdown operation when 'initialized' reaches zero */ + pj_assert(initialized > 0); + if (--initialized != 0) + return; + + /* Call atexit() functions */ + while (atexit_count > 0) { + (*atexit_func[atexit_count-1])(); + --atexit_count; + } + + /* Free exception ID */ + if (PJ_NO_MEMORY_EXCEPTION != -1) { + pj_exception_id_free(PJ_NO_MEMORY_EXCEPTION); + PJ_NO_MEMORY_EXCEPTION = -1; + } + + /* Clear static variables */ + pj_errno_clear_handlers(); + + PjSymbianOS *os = PjSymbianOS::Instance(); + os->Shutdown(); +} + +///////////////////////////////////////////////////////////////////////////// + +class CPollTimeoutTimer : public CActive +{ +public: + static CPollTimeoutTimer* NewL(int msec, TInt prio); + ~CPollTimeoutTimer(); + + virtual void RunL(); + virtual void DoCancel(); + +private: + RTimer rtimer_; + + explicit CPollTimeoutTimer(TInt prio); + void ConstructL(int msec); +}; + +CPollTimeoutTimer::CPollTimeoutTimer(TInt prio) +: CActive(prio) +{ +} + + +CPollTimeoutTimer::~CPollTimeoutTimer() +{ + rtimer_.Close(); +} + +void CPollTimeoutTimer::ConstructL(int msec) +{ + rtimer_.CreateLocal(); + CActiveScheduler::Add(this); + rtimer_.After(iStatus, msec*1000); + SetActive(); +} + +CPollTimeoutTimer* CPollTimeoutTimer::NewL(int msec, TInt prio) +{ + CPollTimeoutTimer *self = new CPollTimeoutTimer(prio); + CleanupStack::PushL(self); + self->ConstructL(msec); + CleanupStack::Pop(self); + + return self; +} + +void CPollTimeoutTimer::RunL() +{ +} + +void CPollTimeoutTimer::DoCancel() +{ + rtimer_.Cancel(); +} + + +/* + * Wait the completion of any Symbian active objects. + */ +PJ_DEF(pj_bool_t) pj_symbianos_poll(int priority, int ms_timeout) +{ + CPollTimeoutTimer *timer = NULL; + + if (priority==-1) + priority = EPriorityNull; + + if (ms_timeout >= 0) { + timer = CPollTimeoutTimer::NewL(ms_timeout, priority); + } + + PjSymbianOS::Instance()->WaitForActiveObjects(priority); + + if (timer) { + bool timer_is_active = timer->IsActive(); + + timer->Cancel(); + + delete timer; + + return timer_is_active ? PJ_TRUE : PJ_FALSE; + + } else { + return PJ_TRUE; + } +} + + +/* + * pj_thread_is_registered() + */ +PJ_DEF(pj_bool_t) pj_thread_is_registered(void) +{ + return PJ_FALSE; +} + + +/* + * Get thread priority value for the thread. + */ +PJ_DEF(int) pj_thread_get_prio(pj_thread_t *thread) +{ + PJ_UNUSED_ARG(thread); + return 1; +} + + +/* + * Set the thread priority. + */ +PJ_DEF(pj_status_t) pj_thread_set_prio(pj_thread_t *thread, int prio) +{ + PJ_UNUSED_ARG(thread); + PJ_UNUSED_ARG(prio); + return PJ_SUCCESS; +} + + +/* + * Get the lowest priority value available on this system. + */ +PJ_DEF(int) pj_thread_get_prio_min(pj_thread_t *thread) +{ + PJ_UNUSED_ARG(thread); + return 1; +} + + +/* + * Get the highest priority value available on this system. + */ +PJ_DEF(int) pj_thread_get_prio_max(pj_thread_t *thread) +{ + PJ_UNUSED_ARG(thread); + return 1; +} + + +/* + * pj_thread_get_os_handle() + */ +PJ_DEF(void*) pj_thread_get_os_handle(pj_thread_t *thread) +{ + PJ_UNUSED_ARG(thread); + return NULL; +} + +/* + * pj_thread_register(..) + */ +PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, + pj_thread_desc desc, + pj_thread_t **thread_ptr) +{ + PJ_UNUSED_ARG(cstr_thread_name); + PJ_UNUSED_ARG(desc); + PJ_UNUSED_ARG(thread_ptr); + return PJ_EINVALIDOP; +} + + +/* + * pj_thread_create(...) + */ +PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, + const char *thread_name, + pj_thread_proc *proc, + void *arg, + pj_size_t stack_size, + unsigned flags, + pj_thread_t **ptr_thread) +{ + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(thread_name); + PJ_UNUSED_ARG(proc); + PJ_UNUSED_ARG(arg); + PJ_UNUSED_ARG(stack_size); + PJ_UNUSED_ARG(flags); + PJ_UNUSED_ARG(ptr_thread); + + /* Sorry mate, we don't support threading */ + return PJ_ENOTSUP; +} + +/* + * pj_thread-get_name() + */ +PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p) +{ + pj_assert(p == &main_thread); + return p->obj_name; +} + +/* + * pj_thread_resume() + */ +PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) +{ + PJ_UNUSED_ARG(p); + return PJ_EINVALIDOP; +} + +/* + * pj_thread_this() + */ +PJ_DEF(pj_thread_t*) pj_thread_this(void) +{ + return &main_thread; +} + +/* + * pj_thread_join() + */ +PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *rec) +{ + PJ_UNUSED_ARG(rec); + return PJ_EINVALIDOP; +} + +/* + * pj_thread_destroy() + */ +PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *rec) +{ + PJ_UNUSED_ARG(rec); + return PJ_EINVALIDOP; +} + +/* + * pj_thread_sleep() + */ +PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) +{ + User::After(msec*1000); + + return PJ_SUCCESS; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * pj_thread_local_alloc() + */ + +PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index) +{ + unsigned i; + + /* Find unused TLS variable */ + for (i=0; i= 0 && index < (int)PJ_ARRAY_SIZE(tls_vars) && + tls_vars[index] != 0, return); + + tls_vars[index] = 0; +} + + +/* + * pj_thread_local_set() + */ +PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value) +{ + pj_thread_t *rec = pj_thread_this(); + + PJ_ASSERT_RETURN(index >= 0 && index < (int)PJ_ARRAY_SIZE(tls_vars) && + tls_vars[index] != 0, PJ_EINVAL); + + rec->tls_values[index] = value; + return PJ_SUCCESS; +} + +/* + * pj_thread_local_get() + */ +PJ_DEF(void*) pj_thread_local_get(long index) +{ + pj_thread_t *rec = pj_thread_this(); + + PJ_ASSERT_RETURN(index >= 0 && index < (int)PJ_ARRAY_SIZE(tls_vars) && + tls_vars[index] != 0, NULL); + + return rec->tls_values[index]; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Create atomic variable. + */ +PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, + pj_atomic_value_t initial, + pj_atomic_t **atomic ) +{ + *atomic = (pj_atomic_t*)pj_pool_alloc(pool, sizeof(struct pj_atomic_t)); + (*atomic)->value = initial; + return PJ_SUCCESS; +} + + +/* + * Destroy atomic variable. + */ +PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var ) +{ + PJ_UNUSED_ARG(atomic_var); + return PJ_SUCCESS; +} + + +/* + * Set the value of an atomic type, and return the previous value. + */ +PJ_DEF(void) pj_atomic_set( pj_atomic_t *atomic_var, + pj_atomic_value_t value) +{ + atomic_var->value = value; +} + + +/* + * Get the value of an atomic type. + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var) +{ + return atomic_var->value; +} + + +/* + * Increment the value of an atomic type. + */ +PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var) +{ + ++atomic_var->value; +} + + +/* + * Increment the value of an atomic type and get the result. + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var) +{ + return ++atomic_var->value; +} + + +/* + * Decrement the value of an atomic type. + */ +PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var) +{ + --atomic_var->value; +} + + +/* + * Decrement the value of an atomic type and get the result. + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var) +{ + return --atomic_var->value; +} + + +/* + * Add a value to an atomic type. + */ +PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var, + pj_atomic_value_t value) +{ + atomic_var->value += value; +} + + +/* + * Add a value to an atomic type and get the result. + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var, + pj_atomic_value_t value) +{ + atomic_var->value += value; + return atomic_var->value; +} + + + +///////////////////////////////////////////////////////////////////////////// + +PJ_DEF(pj_status_t) pj_mutex_create( pj_pool_t *pool, + const char *name, + int type, + pj_mutex_t **mutex) +{ + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(name); + PJ_UNUSED_ARG(type); + + *mutex = DUMMY_MUTEX; + return PJ_SUCCESS; +} + +/* + * pj_mutex_create_simple() + */ +PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); +} + + +PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex); +} + + +/* + * pj_mutex_lock() + */ +PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) +{ + pj_assert(mutex == DUMMY_MUTEX); + return PJ_SUCCESS; +} + +/* + * pj_mutex_trylock() + */ +PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) +{ + pj_assert(mutex == DUMMY_MUTEX); + return PJ_SUCCESS; +} + +/* + * pj_mutex_unlock() + */ +PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) +{ + pj_assert(mutex == DUMMY_MUTEX); + return PJ_SUCCESS; +} + +/* + * pj_mutex_destroy() + */ +PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) +{ + pj_assert(mutex == DUMMY_MUTEX); + return PJ_SUCCESS; +} + + +///////////////////////////////////////////////////////////////////////////// +/* + * RW Mutex + */ +#include "os_rwmutex.c" + + +///////////////////////////////////////////////////////////////////////////// + +/* + * Enter critical section. + */ +PJ_DEF(void) pj_enter_critical_section(void) +{ + /* Nothing to do */ +} + + +/* + * Leave critical section. + */ +PJ_DEF(void) pj_leave_critical_section(void) +{ + /* Nothing to do */ +} + + +///////////////////////////////////////////////////////////////////////////// + +/* + * Create semaphore. + */ +PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_sem_t **p_sem) +{ + pj_sem_t *sem; + + PJ_UNUSED_ARG(name); + + sem = (pj_sem_t*) pj_pool_zalloc(pool, sizeof(pj_sem_t)); + sem->value = initial; + sem->max = max; + + *p_sem = sem; + + return PJ_SUCCESS; +} + + +/* + * Wait for semaphore. + */ +PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) +{ + if (sem->value > 0) { + sem->value--; + return PJ_SUCCESS; + } else { + pj_assert(!"Unexpected!"); + return PJ_EINVALIDOP; + } +} + + +/* + * Try wait for semaphore. + */ +PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) +{ + if (sem->value > 0) { + sem->value--; + return PJ_SUCCESS; + } else { + pj_assert(!"Unexpected!"); + return PJ_EINVALIDOP; + } +} + + +/* + * Release semaphore. + */ +PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) +{ + sem->value++; + return PJ_SUCCESS; +} + + +/* + * Destroy semaphore. + */ +PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) +{ + PJ_UNUSED_ARG(sem); + return PJ_SUCCESS; +} + + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0 +/* + * The implementation of stack checking. + */ +PJ_DEF(void) pj_thread_check_stack(const char *file, int line) +{ + char stk_ptr; + pj_uint32_t usage; + pj_thread_t *thread = pj_thread_this(); + + pj_assert(thread); + + /* Calculate current usage. */ + usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start : + thread->stk_start - &stk_ptr; + + /* Assert if stack usage is dangerously high. */ + pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128)); + + /* Keep statistic. */ + if (usage > thread->stk_max_usage) { + thread->stk_max_usage = usage; + thread->caller_file = file; + thread->caller_line = line; + } +} + +/* + * Get maximum stack usage statistic. + */ +PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread) +{ + return thread->stk_max_usage; +} + +/* + * Dump thread stack status. + */ +PJ_DEF(pj_status_t) pj_thread_get_stack_info(pj_thread_t *thread, + const char **file, + int *line) +{ + pj_assert(thread); + + *file = thread->caller_file; + *line = thread->caller_line; + return 0; +} + +#endif /* PJ_OS_HAS_CHECK_STACK */ + +/* + * pj_run_app() + */ +PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], + unsigned flags) +{ + return (*main_func)(argc, argv); +} diff --git a/pjlib/src/pj/os_core_unix.c b/pjlib/src/pj/os_core_unix.c new file mode 100644 index 0000000..7810eb2 --- /dev/null +++ b/pjlib/src/pj/os_core_unix.c @@ -0,0 +1,1851 @@ +/* $Id: os_core_unix.c 3999 2012-03-30 07:10:13Z bennylp $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + */ +/* + * Contributors: + * - Thanks for Zetron, Inc. (Phil Torre, ptorre@zetron.com) for donating + * the RTEMS port. + */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(PJ_HAS_SEMAPHORE_H) && PJ_HAS_SEMAPHORE_H != 0 +# include +#endif + +#include // getpid() +#include // errno + +#include + +#define THIS_FILE "os_core_unix.c" + +#define SIGNATURE1 0xDEAFBEEF +#define SIGNATURE2 0xDEADC0DE + +struct pj_thread_t +{ + char obj_name[PJ_MAX_OBJ_NAME]; + pthread_t thread; + pj_thread_proc *proc; + void *arg; + pj_uint32_t signature1; + pj_uint32_t signature2; + + pj_mutex_t *suspended_mutex; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + pj_uint32_t stk_size; + pj_uint32_t stk_max_usage; + char *stk_start; + const char *caller_file; + int caller_line; +#endif +}; + +struct pj_atomic_t +{ + pj_mutex_t *mutex; + pj_atomic_value_t value; +}; + +struct pj_mutex_t +{ + pthread_mutex_t mutex; + char obj_name[PJ_MAX_OBJ_NAME]; +#if PJ_DEBUG + int nesting_level; + pj_thread_t *owner; + char owner_name[PJ_MAX_OBJ_NAME]; +#endif +}; + +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 +struct pj_sem_t +{ + sem_t *sem; + char obj_name[PJ_MAX_OBJ_NAME]; +}; +#endif /* PJ_HAS_SEMAPHORE */ + +#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 +struct pj_event_t +{ + char obj_name[PJ_MAX_OBJ_NAME]; +}; +#endif /* PJ_HAS_EVENT_OBJ */ + + +/* + * Flag and reference counter for PJLIB instance. + */ +static int initialized; + +#if PJ_HAS_THREADS + static pj_thread_t main_thread; + static long thread_tls_id; + static pj_mutex_t critical_section; +#else +# define MAX_THREADS 32 + static int tls_flag[MAX_THREADS]; + static void *tls[MAX_THREADS]; +#endif + +static unsigned atexit_count; +static void (*atexit_func[32])(void); + +static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type); + +/* + * pj_init(void). + * Init PJLIB! + */ +PJ_DEF(pj_status_t) pj_init(void) +{ + char dummy_guid[PJ_GUID_MAX_LENGTH]; + pj_str_t guid; + pj_status_t rc; + + /* Check if PJLIB have been initialized */ + if (initialized) { + ++initialized; + return PJ_SUCCESS; + } + +#if PJ_HAS_THREADS + /* Init this thread's TLS. */ + if ((rc=pj_thread_init()) != 0) { + return rc; + } + + /* Critical section. */ + if ((rc=init_mutex(&critical_section, "critsec", PJ_MUTEX_RECURSE)) != 0) + return rc; + +#endif + + /* Init logging */ + pj_log_init(); + + /* Initialize exception ID for the pool. + * Must do so after critical section is configured. + */ + rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); + if (rc != PJ_SUCCESS) + return rc; + + /* Init random seed. */ + /* Or probably not. Let application in charge of this */ + /* pj_srand( clock() ); */ + + /* Startup GUID. */ + guid.ptr = dummy_guid; + pj_generate_unique_string( &guid ); + + /* Startup timestamp */ +#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 + { + pj_timestamp dummy_ts; + if ((rc=pj_get_timestamp(&dummy_ts)) != 0) { + return rc; + } + } +#endif + + /* Flag PJLIB as initialized */ + ++initialized; + pj_assert(initialized == 1); + + PJ_LOG(4,(THIS_FILE, "pjlib %s for POSIX initialized", + PJ_VERSION)); + + return PJ_SUCCESS; +} + +/* + * pj_atexit() + */ +PJ_DEF(pj_status_t) pj_atexit(void (*func)(void)) +{ + if (atexit_count >= PJ_ARRAY_SIZE(atexit_func)) + return PJ_ETOOMANY; + + atexit_func[atexit_count++] = func; + return PJ_SUCCESS; +} + +/* + * pj_shutdown(void) + */ +PJ_DEF(void) pj_shutdown() +{ + int i; + + /* Only perform shutdown operation when 'initialized' reaches zero */ + pj_assert(initialized > 0); + if (--initialized != 0) + return; + + /* Call atexit() functions */ + for (i=atexit_count-1; i>=0; --i) { + (*atexit_func[i])(); + } + atexit_count = 0; + + /* Free exception ID */ + if (PJ_NO_MEMORY_EXCEPTION != -1) { + pj_exception_id_free(PJ_NO_MEMORY_EXCEPTION); + PJ_NO_MEMORY_EXCEPTION = -1; + } + +#if PJ_HAS_THREADS + /* Destroy PJLIB critical section */ + pj_mutex_destroy(&critical_section); + + /* Free PJLIB TLS */ + if (thread_tls_id != -1) { + pj_thread_local_free(thread_tls_id); + thread_tls_id = -1; + } + + /* Ticket #1132: Assertion when (re)starting PJLIB on different thread */ + pj_bzero(&main_thread, sizeof(main_thread)); +#endif + + /* Clear static variables */ + pj_errno_clear_handlers(); +} + + +/* + * pj_getpid(void) + */ +PJ_DEF(pj_uint32_t) pj_getpid(void) +{ + PJ_CHECK_STACK(); + return getpid(); +} + +/* + * Check if this thread has been registered to PJLIB. + */ +PJ_DEF(pj_bool_t) pj_thread_is_registered(void) +{ +#if PJ_HAS_THREADS + return pj_thread_local_get(thread_tls_id) != 0; +#else + pj_assert("pj_thread_is_registered() called in non-threading mode!"); + return PJ_TRUE; +#endif +} + + +/* + * Get thread priority value for the thread. + */ +PJ_DEF(int) pj_thread_get_prio(pj_thread_t *thread) +{ +#if PJ_HAS_THREADS + struct sched_param param; + int policy; + int rc; + + rc = pthread_getschedparam (thread->thread, &policy, ¶m); + if (rc != 0) + return -1; + + return param.sched_priority; +#else + PJ_UNUSED_ARG(thread); + return 1; +#endif +} + + +/* + * Set the thread priority. + */ +PJ_DEF(pj_status_t) pj_thread_set_prio(pj_thread_t *thread, int prio) +{ +#if PJ_HAS_THREADS + struct sched_param param; + int policy; + int rc; + + rc = pthread_getschedparam (thread->thread, &policy, ¶m); + if (rc != 0) + return PJ_RETURN_OS_ERROR(rc); + + param.sched_priority = prio; + + rc = pthread_setschedparam(thread->thread, policy, ¶m); + if (rc != 0) + return PJ_RETURN_OS_ERROR(rc); + + return PJ_SUCCESS; +#else + PJ_UNUSED_ARG(thread); + PJ_UNUSED_ARG(prio); + pj_assert("pj_thread_set_prio() called in non-threading mode!"); + return 1; +#endif +} + + +/* + * Get the lowest priority value available on this system. + */ +PJ_DEF(int) pj_thread_get_prio_min(pj_thread_t *thread) +{ + struct sched_param param; + int policy; + int rc; + + rc = pthread_getschedparam(thread->thread, &policy, ¶m); + if (rc != 0) + return -1; + +#if defined(_POSIX_PRIORITY_SCHEDULING) + return sched_get_priority_min(policy); +#elif defined __OpenBSD__ + /* Thread prio min/max are declared in OpenBSD private hdr */ + return 0; +#else + pj_assert("pj_thread_get_prio_min() not supported!"); + return 0; +#endif +} + + +/* + * Get the highest priority value available on this system. + */ +PJ_DEF(int) pj_thread_get_prio_max(pj_thread_t *thread) +{ + struct sched_param param; + int policy; + int rc; + + rc = pthread_getschedparam(thread->thread, &policy, ¶m); + if (rc != 0) + return -1; + +#if defined(_POSIX_PRIORITY_SCHEDULING) + return sched_get_priority_max(policy); +#elif defined __OpenBSD__ + /* Thread prio min/max are declared in OpenBSD private hdr */ + return 31; +#else + pj_assert("pj_thread_get_prio_max() not supported!"); + return 0; +#endif +} + + +/* + * Get native thread handle + */ +PJ_DEF(void*) pj_thread_get_os_handle(pj_thread_t *thread) +{ + PJ_ASSERT_RETURN(thread, NULL); + +#if PJ_HAS_THREADS + return &thread->thread; +#else + pj_assert("pj_thread_is_registered() called in non-threading mode!"); + return NULL; +#endif +} + +/* + * pj_thread_register(..) + */ +PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, + pj_thread_desc desc, + pj_thread_t **ptr_thread) +{ +#if PJ_HAS_THREADS + char stack_ptr; + pj_status_t rc; + pj_thread_t *thread = (pj_thread_t *)desc; + pj_str_t thread_name = pj_str((char*)cstr_thread_name); + + /* Size sanity check. */ + if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { + pj_assert(!"Not enough pj_thread_desc size!"); + return PJ_EBUG; + } + + /* Warn if this thread has been registered before */ + if (pj_thread_local_get (thread_tls_id) != 0) { + // 2006-02-26 bennylp: + // This wouldn't work in all cases!. + // If thread is created by external module (e.g. sound thread), + // thread may be reused while the pool used for the thread descriptor + // has been deleted by application. + //*thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id); + //return PJ_SUCCESS; + PJ_LOG(4,(THIS_FILE, "Info: possibly re-registering existing " + "thread")); + } + + /* On the other hand, also warn if the thread descriptor buffer seem to + * have been used to register other threads. + */ + pj_assert(thread->signature1 != SIGNATURE1 || + thread->signature2 != SIGNATURE2 || + (thread->thread == pthread_self())); + + /* Initialize and set the thread entry. */ + pj_bzero(desc, sizeof(struct pj_thread_t)); + thread->thread = pthread_self(); + thread->signature1 = SIGNATURE1; + thread->signature2 = SIGNATURE2; + + if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) + pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), + cstr_thread_name, thread->thread); + else + pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), + "thr%p", (void*)thread->thread); + + rc = pj_thread_local_set(thread_tls_id, thread); + if (rc != PJ_SUCCESS) { + pj_bzero(desc, sizeof(struct pj_thread_t)); + return rc; + } + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + thread->stk_start = &stack_ptr; + thread->stk_size = 0xFFFFFFFFUL; + thread->stk_max_usage = 0; +#else + stack_ptr = '\0'; +#endif + + *ptr_thread = thread; + return PJ_SUCCESS; +#else + pj_thread_t *thread = (pj_thread_t*)desc; + *ptr_thread = thread; + return PJ_SUCCESS; +#endif +} + +/* + * pj_thread_init(void) + */ +pj_status_t pj_thread_init(void) +{ +#if PJ_HAS_THREADS + pj_status_t rc; + pj_thread_t *dummy; + + rc = pj_thread_local_alloc(&thread_tls_id ); + if (rc != PJ_SUCCESS) { + return rc; + } + return pj_thread_register("thr%p", (long*)&main_thread, &dummy); +#else + PJ_LOG(2,(THIS_FILE, "Thread init error. Threading is not enabled!")); + return PJ_EINVALIDOP; +#endif +} + +#if PJ_HAS_THREADS +/* + * thread_main() + * + * This is the main entry for all threads. + */ +static void *thread_main(void *param) +{ + pj_thread_t *rec = (pj_thread_t*)param; + void *result; + pj_status_t rc; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + rec->stk_start = (char*)&rec; +#endif + + /* Set current thread id. */ + rc = pj_thread_local_set(thread_tls_id, rec); + if (rc != PJ_SUCCESS) { + pj_assert(!"Thread TLS ID is not set (pj_init() error?)"); + } + + /* Check if suspension is required. */ + if (rec->suspended_mutex) { + pj_mutex_lock(rec->suspended_mutex); + pj_mutex_unlock(rec->suspended_mutex); + } + + PJ_LOG(6,(rec->obj_name, "Thread started")); + + /* Call user's entry! */ + result = (void*)(long)(*rec->proc)(rec->arg); + + /* Done. */ + PJ_LOG(6,(rec->obj_name, "Thread quitting")); + + return result; +} +#endif + +/* + * pj_thread_create(...) + */ +PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, + const char *thread_name, + pj_thread_proc *proc, + void *arg, + pj_size_t stack_size, + unsigned flags, + pj_thread_t **ptr_thread) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec; + pthread_attr_t thread_attr; + void *stack_addr; + int rc; + + PJ_UNUSED_ARG(stack_addr); + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL); + + /* Create thread record and assign name for the thread */ + rec = (struct pj_thread_t*) pj_pool_zalloc(pool, sizeof(pj_thread_t)); + PJ_ASSERT_RETURN(rec, PJ_ENOMEM); + + /* Set name. */ + if (!thread_name) + thread_name = "thr%p"; + + if (strchr(thread_name, '%')) { + pj_ansi_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec); + } else { + strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME); + rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + /* Set default stack size */ + if (stack_size == 0) + stack_size = PJ_THREAD_DEFAULT_STACK_SIZE; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + rec->stk_size = stack_size; + rec->stk_max_usage = 0; +#endif + + /* Emulate suspended thread with mutex. */ + if (flags & PJ_THREAD_SUSPENDED) { + rc = pj_mutex_create_simple(pool, NULL, &rec->suspended_mutex); + if (rc != PJ_SUCCESS) { + return rc; + } + + pj_mutex_lock(rec->suspended_mutex); + } else { + pj_assert(rec->suspended_mutex == NULL); + } + + + /* Init thread attributes */ + pthread_attr_init(&thread_attr); + +#if defined(PJ_THREAD_SET_STACK_SIZE) && PJ_THREAD_SET_STACK_SIZE!=0 + /* Set thread's stack size */ + rc = pthread_attr_setstacksize(&thread_attr, stack_size); + if (rc != 0) + return PJ_RETURN_OS_ERROR(rc); +#endif /* PJ_THREAD_SET_STACK_SIZE */ + + +#if defined(PJ_THREAD_ALLOCATE_STACK) && PJ_THREAD_ALLOCATE_STACK!=0 + /* Allocate memory for the stack */ + stack_addr = pj_pool_alloc(pool, stack_size); + PJ_ASSERT_RETURN(stack_addr, PJ_ENOMEM); + + rc = pthread_attr_setstackaddr(&thread_attr, stack_addr); + if (rc != 0) + return PJ_RETURN_OS_ERROR(rc); +#endif /* PJ_THREAD_ALLOCATE_STACK */ + + + /* Create the thread. */ + rec->proc = proc; + rec->arg = arg; + rc = pthread_create( &rec->thread, &thread_attr, &thread_main, rec); + if (rc != 0) { + return PJ_RETURN_OS_ERROR(rc); + } + + *ptr_thread = rec; + + PJ_LOG(6, (rec->obj_name, "Thread created")); + return PJ_SUCCESS; +#else + pj_assert(!"Threading is disabled!"); + return PJ_EINVALIDOP; +#endif +} + +/* + * pj_thread-get_name() + */ +PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec = (pj_thread_t*)p; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, ""); + + return rec->obj_name; +#else + return ""; +#endif +} + +/* + * pj_thread_resume() + */ +PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) +{ + pj_status_t rc; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, PJ_EINVAL); + + rc = pj_mutex_unlock(p->suspended_mutex); + + return rc; +} + +/* + * pj_thread_this() + */ +PJ_DEF(pj_thread_t*) pj_thread_this(void) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec = (pj_thread_t*)pj_thread_local_get(thread_tls_id); + + if (rec == NULL) { + pj_assert(!"Calling pjlib from unknown/external thread. You must " + "register external threads with pj_thread_register() " + "before calling any pjlib functions."); + } + + /* + * MUST NOT check stack because this function is called + * by PJ_CHECK_STACK() itself!!! + * + */ + + return rec; +#else + pj_assert(!"Threading is not enabled!"); + return NULL; +#endif +} + +/* + * pj_thread_join() + */ +PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec = (pj_thread_t *)p; + void *ret; + int result; + + PJ_CHECK_STACK(); + + PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name)); + result = pthread_join( rec->thread, &ret); + + if (result == 0) + return PJ_SUCCESS; + else { + /* Calling pthread_join() on a thread that no longer exists and + * getting back ESRCH isn't an error (in this context). + * Thanks Phil Torre . + */ + return result==ESRCH ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(result); + } +#else + PJ_CHECK_STACK(); + pj_assert(!"No multithreading support!"); + return PJ_EINVALIDOP; +#endif +} + +/* + * pj_thread_destroy() + */ +PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p) +{ + PJ_CHECK_STACK(); + + /* Destroy mutex used to suspend thread */ + if (p->suspended_mutex) { + pj_mutex_destroy(p->suspended_mutex); + p->suspended_mutex = NULL; + } + + return PJ_SUCCESS; +} + +/* + * pj_thread_sleep() + */ +PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) +{ +/* TODO: should change this to something like PJ_OS_HAS_NANOSLEEP */ +#if defined(PJ_RTEMS) && PJ_RTEMS!=0 + enum { NANOSEC_PER_MSEC = 1000000 }; + struct timespec req; + + PJ_CHECK_STACK(); + req.tv_sec = msec / 1000; + req.tv_nsec = (msec % 1000) * NANOSEC_PER_MSEC; + + if (nanosleep(&req, NULL) == 0) + return PJ_SUCCESS; + + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + PJ_CHECK_STACK(); + + pj_set_os_error(0); + + usleep(msec * 1000); + + /* MacOS X (reported on 10.5) seems to always set errno to ETIMEDOUT. + * It does so because usleep() is declared to return int, and we're + * supposed to check for errno only when usleep() returns non-zero. + * Unfortunately, usleep() is declared to return void in other platforms + * so it's not possible to always check for the return value (unless + * we add a detection routine in autoconf). + * + * As a workaround, here we check if ETIMEDOUT is returned and + * return successfully if it is. + */ + if (pj_get_native_os_error() == ETIMEDOUT) + return PJ_SUCCESS; + + return pj_get_os_error(); + +#endif /* PJ_RTEMS */ +} + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 +/* + * pj_thread_check_stack() + * Implementation for PJ_CHECK_STACK() + */ +PJ_DEF(void) pj_thread_check_stack(const char *file, int line) +{ + char stk_ptr; + pj_uint32_t usage; + pj_thread_t *thread = pj_thread_this(); + + /* Calculate current usage. */ + usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start : + thread->stk_start - &stk_ptr; + + /* Assert if stack usage is dangerously high. */ + pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128)); + + /* Keep statistic. */ + if (usage > thread->stk_max_usage) { + thread->stk_max_usage = usage; + thread->caller_file = file; + thread->caller_line = line; + } +} + +/* + * pj_thread_get_stack_max_usage() + */ +PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread) +{ + return thread->stk_max_usage; +} + +/* + * pj_thread_get_stack_info() + */ +PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread, + const char **file, + int *line ) +{ + pj_assert(thread); + + *file = thread->caller_file; + *line = thread->caller_line; + return 0; +} + +#endif /* PJ_OS_HAS_CHECK_STACK */ + +/////////////////////////////////////////////////////////////////////////////// +/* + * pj_atomic_create() + */ +PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, + pj_atomic_value_t initial, + pj_atomic_t **ptr_atomic) +{ + pj_status_t rc; + pj_atomic_t *atomic_var; + + atomic_var = PJ_POOL_ZALLOC_T(pool, pj_atomic_t); + + PJ_ASSERT_RETURN(atomic_var, PJ_ENOMEM); + +#if PJ_HAS_THREADS + rc = pj_mutex_create(pool, "atm%p", PJ_MUTEX_SIMPLE, &atomic_var->mutex); + if (rc != PJ_SUCCESS) + return rc; +#endif + atomic_var->value = initial; + + *ptr_atomic = atomic_var; + return PJ_SUCCESS; +} + +/* + * pj_atomic_destroy() + */ +PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var ) +{ + PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL); +#if PJ_HAS_THREADS + return pj_mutex_destroy( atomic_var->mutex ); +#else + return 0; +#endif +} + +/* + * pj_atomic_set() + */ +PJ_DEF(void) pj_atomic_set(pj_atomic_t *atomic_var, pj_atomic_value_t value) +{ + PJ_CHECK_STACK(); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + atomic_var->value = value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif +} + +/* + * pj_atomic_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var) +{ + pj_atomic_value_t oldval; + + PJ_CHECK_STACK(); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + oldval = atomic_var->value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif + return oldval; +} + +/* + * pj_atomic_inc_and_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var) +{ + pj_atomic_value_t new_value; + + PJ_CHECK_STACK(); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + new_value = ++atomic_var->value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif + + return new_value; +} +/* + * pj_atomic_inc() + */ +PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var) +{ + pj_atomic_inc_and_get(atomic_var); +} + +/* + * pj_atomic_dec_and_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var) +{ + pj_atomic_value_t new_value; + + PJ_CHECK_STACK(); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + new_value = --atomic_var->value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif + + return new_value; +} + +/* + * pj_atomic_dec() + */ +PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var) +{ + pj_atomic_dec_and_get(atomic_var); +} + +/* + * pj_atomic_add_and_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var, + pj_atomic_value_t value ) +{ + pj_atomic_value_t new_value; + +#if PJ_HAS_THREADS + pj_mutex_lock(atomic_var->mutex); +#endif + + atomic_var->value += value; + new_value = atomic_var->value; + +#if PJ_HAS_THREADS + pj_mutex_unlock(atomic_var->mutex); +#endif + + return new_value; +} + +/* + * pj_atomic_add() + */ +PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var, + pj_atomic_value_t value ) +{ + pj_atomic_add_and_get(atomic_var, value); +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * pj_thread_local_alloc() + */ +PJ_DEF(pj_status_t) pj_thread_local_alloc(long *p_index) +{ +#if PJ_HAS_THREADS + pthread_key_t key; + int rc; + + PJ_ASSERT_RETURN(p_index != NULL, PJ_EINVAL); + + pj_assert( sizeof(pthread_key_t) <= sizeof(long)); + if ((rc=pthread_key_create(&key, NULL)) != 0) + return PJ_RETURN_OS_ERROR(rc); + + *p_index = key; + return PJ_SUCCESS; +#else + int i; + for (i=0; i= 0 && index < MAX_THREADS); + tls[index] = value; + return PJ_SUCCESS; +#endif +} + +PJ_DEF(void*) pj_thread_local_get(long index) +{ + //Can't check stack because this function is called + //by PJ_CHECK_STACK() itself!!! + //PJ_CHECK_STACK(); +#if PJ_HAS_THREADS + return pthread_getspecific(index); +#else + pj_assert(index >= 0 && index < MAX_THREADS); + return tls[index]; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(void) pj_enter_critical_section(void) +{ +#if PJ_HAS_THREADS + pj_mutex_lock(&critical_section); +#endif +} + +PJ_DEF(void) pj_leave_critical_section(void) +{ +#if PJ_HAS_THREADS + pj_mutex_unlock(&critical_section); +#endif +} + + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_LINUX) && PJ_LINUX!=0 +PJ_BEGIN_DECL +PJ_DECL(int) pthread_mutexattr_settype(pthread_mutexattr_t*,int); +PJ_END_DECL +#endif + +static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type) +{ +#if PJ_HAS_THREADS + pthread_mutexattr_t attr; + int rc; + + PJ_CHECK_STACK(); + + rc = pthread_mutexattr_init(&attr); + if (rc != 0) + return PJ_RETURN_OS_ERROR(rc); + + if (type == PJ_MUTEX_SIMPLE) { +#if (defined(PJ_LINUX) && PJ_LINUX!=0) || \ + defined(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE) + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_FAST_NP); +#elif (defined(PJ_RTEMS) && PJ_RTEMS!=0) || \ + defined(PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE) + /* Nothing to do, default is simple */ +#else + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); +#endif + } else { +#if (defined(PJ_LINUX) && PJ_LINUX!=0) || \ + defined(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE) + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); +#elif (defined(PJ_RTEMS) && PJ_RTEMS!=0) || \ + defined(PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE) + // Phil Torre : + // The RTEMS implementation of POSIX mutexes doesn't include + // pthread_mutexattr_settype(), so what follows is a hack + // until I get RTEMS patched to support the set/get functions. + // + // More info: + // newlib's pthread also lacks pthread_mutexattr_settype(), + // but it seems to have mutexattr.recursive. + PJ_TODO(FIX_RTEMS_RECURSIVE_MUTEX_TYPE) + attr.recursive = 1; +#else + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); +#endif + } + + if (rc != 0) { + return PJ_RETURN_OS_ERROR(rc); + } + + rc = pthread_mutex_init(&mutex->mutex, &attr); + if (rc != 0) { + return PJ_RETURN_OS_ERROR(rc); + } + + rc = pthread_mutexattr_destroy(&attr); + if (rc != 0) { + pj_status_t status = PJ_RETURN_OS_ERROR(rc); + pthread_mutex_destroy(&mutex->mutex); + return status; + } + +#if PJ_DEBUG + /* Set owner. */ + mutex->nesting_level = 0; + mutex->owner = NULL; +#endif + + /* Set name. */ + if (!name) { + name = "mtx%p"; + } + if (strchr(name, '%')) { + pj_ansi_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex); + } else { + strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME); + mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (mutex->obj_name, "Mutex created")); + return PJ_SUCCESS; +#else /* PJ_HAS_THREADS */ + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_create() + */ +PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool, + const char *name, + int type, + pj_mutex_t **ptr_mutex) +{ +#if PJ_HAS_THREADS + pj_status_t rc; + pj_mutex_t *mutex; + + PJ_ASSERT_RETURN(pool && ptr_mutex, PJ_EINVAL); + + mutex = PJ_POOL_ALLOC_T(pool, pj_mutex_t); + PJ_ASSERT_RETURN(mutex, PJ_ENOMEM); + + if ((rc=init_mutex(mutex, name, type)) != PJ_SUCCESS) + return rc; + + *ptr_mutex = mutex; + return PJ_SUCCESS; +#else /* PJ_HAS_THREADS */ + *ptr_mutex = (pj_mutex_t*)1; + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_create_simple() + */ +PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); +} + +/* + * pj_mutex_create_recursive() + */ +PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex); +} + +/* + * pj_mutex_lock() + */ +PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + +#if PJ_DEBUG + PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting (mutex owner=%s)", + pj_thread_this()->obj_name, + mutex->owner_name)); +#else + PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting", + pj_thread_this()->obj_name)); +#endif + + status = pthread_mutex_lock( &mutex->mutex ); + + +#if PJ_DEBUG + if (status == PJ_SUCCESS) { + mutex->owner = pj_thread_this(); + pj_ansi_strcpy(mutex->owner_name, mutex->owner->obj_name); + ++mutex->nesting_level; + } + + PJ_LOG(6,(mutex->obj_name, + (status==0 ? + "Mutex acquired by thread %s (level=%d)" : + "Mutex acquisition FAILED by %s (level=%d)"), + pj_thread_this()->obj_name, + mutex->nesting_level)); +#else + PJ_LOG(6,(mutex->obj_name, + (status==0 ? "Mutex acquired by thread %s" : "FAILED by %s"), + pj_thread_this()->obj_name)); +#endif + + if (status == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(status); +#else /* PJ_HAS_THREADS */ + pj_assert( mutex == (pj_mutex_t*)1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_unlock() + */ +PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + +#if PJ_DEBUG + pj_assert(mutex->owner == pj_thread_this()); + if (--mutex->nesting_level == 0) { + mutex->owner = NULL; + mutex->owner_name[0] = '\0'; + } + + PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s (level=%d)", + pj_thread_this()->obj_name, + mutex->nesting_level)); +#else + PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s", + pj_thread_this()->obj_name)); +#endif + + status = pthread_mutex_unlock( &mutex->mutex ); + if (status == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(status); + +#else /* PJ_HAS_THREADS */ + pj_assert( mutex == (pj_mutex_t*)1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_trylock() + */ +PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + int status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is trying", + pj_thread_this()->obj_name)); + + status = pthread_mutex_trylock( &mutex->mutex ); + + if (status==0) { +#if PJ_DEBUG + mutex->owner = pj_thread_this(); + pj_ansi_strcpy(mutex->owner_name, mutex->owner->obj_name); + ++mutex->nesting_level; + + PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s (level=%d)", + pj_thread_this()->obj_name, + mutex->nesting_level)); +#else + PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s", + pj_thread_this()->obj_name)); +#endif + } else { + PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s's trylock() failed", + pj_thread_this()->obj_name)); + } + + if (status==0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(status); +#else /* PJ_HAS_THREADS */ + pj_assert( mutex == (pj_mutex_t*)1); + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_destroy() + */ +PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) +{ + enum { RETRY = 4 }; + int status = 0; + unsigned retry; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + +#if PJ_HAS_THREADS + PJ_LOG(6,(mutex->obj_name, "Mutex destroyed by thread %s", + pj_thread_this()->obj_name)); + + for (retry=0; retrymutex ); + if (status == PJ_SUCCESS) + break; + else if (retrymutex); + } + + if (status == 0) + return PJ_SUCCESS; + else { + return PJ_RETURN_OS_ERROR(status); + } +#else + pj_assert( mutex == (pj_mutex_t*)1 ); + status = PJ_SUCCESS; + return status; +#endif +} + +#if PJ_DEBUG +PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + return mutex->owner == pj_thread_this(); +#else + return 1; +#endif +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/* + * Include Read/Write mutex emulation for POSIX platforms that lack it (e.g. + * RTEMS). Otherwise use POSIX rwlock. + */ +#if defined(PJ_EMULATE_RWMUTEX) && PJ_EMULATE_RWMUTEX!=0 + /* We need semaphore functionality to emulate rwmutex */ +# if !defined(PJ_HAS_SEMAPHORE) || PJ_HAS_SEMAPHORE==0 +# error "Semaphore support needs to be enabled to emulate rwmutex" +# endif +# include "os_rwmutex.c" +#else +struct pj_rwmutex_t +{ + pthread_rwlock_t rwlock; +}; + +PJ_DEF(pj_status_t) pj_rwmutex_create(pj_pool_t *pool, const char *name, + pj_rwmutex_t **p_mutex) +{ + pj_rwmutex_t *rwm; + pj_status_t status; + + PJ_UNUSED_ARG(name); + + rwm = PJ_POOL_ALLOC_T(pool, pj_rwmutex_t); + PJ_ASSERT_RETURN(rwm, PJ_ENOMEM); + + status = pthread_rwlock_init(&rwm->rwlock, NULL); + if (status != 0) + return PJ_RETURN_OS_ERROR(status); + + *p_mutex = rwm; + return PJ_SUCCESS; +} + +/* + * Lock the mutex for reading. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_lock_read(pj_rwmutex_t *mutex) +{ + pj_status_t status; + + status = pthread_rwlock_rdlock(&mutex->rwlock); + if (status != 0) + return PJ_RETURN_OS_ERROR(status); + + return PJ_SUCCESS; +} + +/* + * Lock the mutex for writing. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_lock_write(pj_rwmutex_t *mutex) +{ + pj_status_t status; + + status = pthread_rwlock_wrlock(&mutex->rwlock); + if (status != 0) + return PJ_RETURN_OS_ERROR(status); + + return PJ_SUCCESS; +} + +/* + * Release read lock. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_unlock_read(pj_rwmutex_t *mutex) +{ + return pj_rwmutex_unlock_write(mutex); +} + +/* + * Release write lock. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_unlock_write(pj_rwmutex_t *mutex) +{ + pj_status_t status; + + status = pthread_rwlock_unlock(&mutex->rwlock); + if (status != 0) + return PJ_RETURN_OS_ERROR(status); + + return PJ_SUCCESS; +} + +/* + * Destroy reader/writer mutex. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_destroy(pj_rwmutex_t *mutex) +{ + pj_status_t status; + + status = pthread_rwlock_destroy(&mutex->rwlock); + if (status != 0) + return PJ_RETURN_OS_ERROR(status); + + return PJ_SUCCESS; +} + +#endif /* PJ_EMULATE_RWMUTEX */ + + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 + +/* + * pj_sem_create() + */ +PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_sem_t **ptr_sem) +{ +#if PJ_HAS_THREADS + pj_sem_t *sem; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool != NULL && ptr_sem != NULL, PJ_EINVAL); + + sem = PJ_POOL_ALLOC_T(pool, pj_sem_t); + PJ_ASSERT_RETURN(sem, PJ_ENOMEM); + +#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 + /* MacOS X doesn't support anonymous semaphore */ + { + char sem_name[PJ_GUID_MAX_LENGTH+1]; + pj_str_t nam; + + /* We should use SEM_NAME_LEN, but this doesn't seem to be + * declared anywhere? The value here is just from trial and error + * to get the longest name supported. + */ +# define MAX_SEM_NAME_LEN 23 + + /* Create a unique name for the semaphore. */ + if (PJ_GUID_STRING_LENGTH <= MAX_SEM_NAME_LEN) { + nam.ptr = sem_name; + pj_generate_unique_string(&nam); + sem_name[nam.slen] = '\0'; + } else { + pj_create_random_string(sem_name, MAX_SEM_NAME_LEN); + sem_name[MAX_SEM_NAME_LEN] = '\0'; + } + + /* Create semaphore */ + sem->sem = sem_open(sem_name, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR, + initial); + if (sem->sem == SEM_FAILED) + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); + + /* And immediately release the name as we don't need it */ + sem_unlink(sem_name); + } +#else + sem->sem = PJ_POOL_ALLOC_T(pool, sem_t); + if (sem_init( sem->sem, 0, initial) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#endif + + /* Set name. */ + if (!name) { + name = "sem%p"; + } + if (strchr(name, '%')) { + pj_ansi_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem); + } else { + strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME); + sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (sem->obj_name, "Semaphore created")); + + *ptr_sem = sem; + return PJ_SUCCESS; +#else + *ptr_sem = (pj_sem_t*)1; + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_wait() + */ +PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting", + pj_thread_this()->obj_name)); + + result = sem_wait( sem->sem ); + + if (result == 0) { + PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", + pj_thread_this()->obj_name)); + } else { + PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire", + pj_thread_this()->obj_name)); + } + + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*) 1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_trywait() + */ +PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + result = sem_trywait( sem->sem ); + + if (result == 0) { + PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", + pj_thread_this()->obj_name)); + } + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*)1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_post() + */ +PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s", + pj_thread_this()->obj_name)); + result = sem_post( sem->sem ); + + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*) 1); + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_destroy() + */ +PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s", + pj_thread_this()->obj_name)); +#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 + result = sem_close( sem->sem ); +#else + result = sem_destroy( sem->sem ); +#endif + + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*) 1 ); + return PJ_SUCCESS; +#endif +} + +#endif /* PJ_HAS_SEMAPHORE */ + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 + +/* + * pj_event_create() + */ +PJ_DEF(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name, + pj_bool_t manual_reset, pj_bool_t initial, + pj_event_t **ptr_event) +{ + pj_assert(!"Not supported!"); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(name); + PJ_UNUSED_ARG(manual_reset); + PJ_UNUSED_ARG(initial); + PJ_UNUSED_ARG(ptr_event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_wait() + */ +PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_trywait() + */ +PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_set() + */ +PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_pulse() + */ +PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_reset() + */ +PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_destroy() + */ +PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +#endif /* PJ_HAS_EVENT_OBJ */ + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 +/* + * Terminal + */ + +/** + * Set terminal color. + */ +PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color) +{ + /* put bright prefix to ansi_color */ + char ansi_color[12] = "\033[01;3"; + + if (color & PJ_TERM_COLOR_BRIGHT) { + color ^= PJ_TERM_COLOR_BRIGHT; + } else { + strcpy(ansi_color, "\033[00;3"); + } + + switch (color) { + case 0: + /* black color */ + strcat(ansi_color, "0m"); + break; + case PJ_TERM_COLOR_R: + /* red color */ + strcat(ansi_color, "1m"); + break; + case PJ_TERM_COLOR_G: + /* green color */ + strcat(ansi_color, "2m"); + break; + case PJ_TERM_COLOR_B: + /* blue color */ + strcat(ansi_color, "4m"); + break; + case PJ_TERM_COLOR_R | PJ_TERM_COLOR_G: + /* yellow color */ + strcat(ansi_color, "3m"); + break; + case PJ_TERM_COLOR_R | PJ_TERM_COLOR_B: + /* magenta color */ + strcat(ansi_color, "5m"); + break; + case PJ_TERM_COLOR_G | PJ_TERM_COLOR_B: + /* cyan color */ + strcat(ansi_color, "6m"); + break; + case PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B: + /* white color */ + strcat(ansi_color, "7m"); + break; + default: + /* default console color */ + strcpy(ansi_color, "\033[00m"); + break; + } + + fputs(ansi_color, stdout); + + return PJ_SUCCESS; +} + +/** + * Get current terminal foreground color. + */ +PJ_DEF(pj_color_t) pj_term_get_color(void) +{ + return 0; +} + +#endif /* PJ_TERM_HAS_COLOR */ + +#if !defined(PJ_DARWINOS) || PJ_DARWINOS == 0 +/* + * pj_run_app() + */ +PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], + unsigned flags) +{ + return (*main_func)(argc, argv); +} +#endif diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c new file mode 100644 index 0000000..876a25c --- /dev/null +++ b/pjlib/src/pj/os_core_win32.c @@ -0,0 +1,1456 @@ +/* $Id: os_core_win32.c 3999 2012-03-30 07:10:13Z bennylp $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 +# include +#endif + +#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 +# include +#endif + +/* Activate mutex related logging if PJ_DEBUG_MUTEX is set, otherwise + * use default level 6 logging. + */ +#if defined(PJ_DEBUG_MUTEX) && PJ_DEBUG_MUTEX +# undef PJ_DEBUG +# define PJ_DEBUG 1 +# define LOG_MUTEX(expr) PJ_LOG(5,expr) +#else +# define LOG_MUTEX(expr) PJ_LOG(6,expr) +#endif + +#define THIS_FILE "os_core_win32.c" + +/* + * Implementation of pj_thread_t. + */ +struct pj_thread_t +{ + char obj_name[PJ_MAX_OBJ_NAME]; + HANDLE hthread; + DWORD idthread; + pj_thread_proc *proc; + void *arg; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + pj_uint32_t stk_size; + pj_uint32_t stk_max_usage; + char *stk_start; + const char *caller_file; + int caller_line; +#endif +}; + + +/* + * Implementation of pj_mutex_t. + */ +struct pj_mutex_t +{ +#if PJ_WIN32_WINNT >= 0x0400 + CRITICAL_SECTION crit; +#else + HANDLE hMutex; +#endif + char obj_name[PJ_MAX_OBJ_NAME]; +#if PJ_DEBUG + int nesting_level; + pj_thread_t *owner; +#endif +}; + +/* + * Implementation of pj_sem_t. + */ +typedef struct pj_sem_t +{ + HANDLE hSemaphore; + char obj_name[PJ_MAX_OBJ_NAME]; +} pj_mem_t; + + +/* + * Implementation of pj_event_t. + */ +struct pj_event_t +{ + HANDLE hEvent; + char obj_name[PJ_MAX_OBJ_NAME]; +}; + +/* + * Implementation of pj_atomic_t. + */ +struct pj_atomic_t +{ + long value; +}; + +/* + * Flag and reference counter for PJLIB instance. + */ +static int initialized; + +/* + * Static global variables. + */ +static pj_thread_desc main_thread; +static long thread_tls_id = -1; +static pj_mutex_t critical_section_mutex; +static unsigned atexit_count; +static void (*atexit_func[32])(void); + +/* + * Some static prototypes. + */ +static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name); + + +/* + * pj_init(void). + * Init PJLIB! + */ +PJ_DEF(pj_status_t) pj_init(void) +{ + WSADATA wsa; + char dummy_guid[32]; /* use maximum GUID length */ + pj_str_t guid; + pj_status_t rc; + + /* Check if PJLIB have been initialized */ + if (initialized) { + ++initialized; + return PJ_SUCCESS; + } + + /* Init Winsock.. */ + if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) { + return PJ_RETURN_OS_ERROR(WSAGetLastError()); + } + + /* Init this thread's TLS. */ + if ((rc=pj_thread_init()) != PJ_SUCCESS) { + return rc; + } + + /* Init logging */ + pj_log_init(); + + /* Init random seed. */ + /* Or probably not. Let application in charge of this */ + /* pj_srand( GetCurrentProcessId() ); */ + + /* Initialize critical section. */ + if ((rc=init_mutex(&critical_section_mutex, "pj%p")) != PJ_SUCCESS) + return rc; + + /* Startup GUID. */ + guid.ptr = dummy_guid; + pj_generate_unique_string( &guid ); + + /* Initialize exception ID for the pool. + * Must do so after critical section is configured. + */ + rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); + if (rc != PJ_SUCCESS) + return rc; + + /* Startup timestamp */ +#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 + { + pj_timestamp dummy_ts; + if ((rc=pj_get_timestamp_freq(&dummy_ts)) != PJ_SUCCESS) { + return rc; + } + if ((rc=pj_get_timestamp(&dummy_ts)) != PJ_SUCCESS) { + return rc; + } + } +#endif + + /* Flag PJLIB as initialized */ + ++initialized; + pj_assert(initialized == 1); + + PJ_LOG(4,(THIS_FILE, "pjlib %s for win32 initialized", + PJ_VERSION)); + + return PJ_SUCCESS; +} + +/* + * pj_atexit() + */ +PJ_DEF(pj_status_t) pj_atexit(void (*func)(void)) +{ + if (atexit_count >= PJ_ARRAY_SIZE(atexit_func)) + return PJ_ETOOMANY; + + atexit_func[atexit_count++] = func; + return PJ_SUCCESS; +} + + +/* + * pj_shutdown(void) + */ +PJ_DEF(void) pj_shutdown() +{ + int i; + + /* Only perform shutdown operation when 'initialized' reaches zero */ + pj_assert(initialized > 0); + if (--initialized != 0) + return; + + /* Display stack usage */ +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + { + pj_thread_t *rec = (pj_thread_t*)main_thread; + PJ_LOG(5,(rec->obj_name, "Main thread stack max usage=%u by %s:%d", + rec->stk_max_usage, rec->caller_file, rec->caller_line)); + } +#endif + + /* Call atexit() functions */ + for (i=atexit_count-1; i>=0; --i) { + (*atexit_func[i])(); + } + atexit_count = 0; + + /* Free exception ID */ + if (PJ_NO_MEMORY_EXCEPTION != -1) { + pj_exception_id_free(PJ_NO_MEMORY_EXCEPTION); + PJ_NO_MEMORY_EXCEPTION = -1; + } + + /* Destroy PJLIB critical section */ + pj_mutex_destroy(&critical_section_mutex); + + /* Free PJLIB TLS */ + if (thread_tls_id != -1) { + pj_thread_local_free(thread_tls_id); + thread_tls_id = -1; + } + + /* Clear static variables */ + pj_errno_clear_handlers(); + + /* Ticket #1132: Assertion when (re)starting PJLIB on different thread */ + pj_bzero(main_thread, sizeof(main_thread)); + + /* Shutdown Winsock */ + WSACleanup(); +} + + +/* + * pj_getpid(void) + */ +PJ_DEF(pj_uint32_t) pj_getpid(void) +{ + PJ_CHECK_STACK(); + return GetCurrentProcessId(); +} + +/* + * Check if this thread has been registered to PJLIB. + */ +PJ_DEF(pj_bool_t) pj_thread_is_registered(void) +{ + return pj_thread_local_get(thread_tls_id) != 0; +} + + +/* + * Get thread priority value for the thread. + */ +PJ_DEF(int) pj_thread_get_prio(pj_thread_t *thread) +{ + return GetThreadPriority(thread->hthread); +} + + +/* + * Set the thread priority. + */ +PJ_DEF(pj_status_t) pj_thread_set_prio(pj_thread_t *thread, int prio) +{ +#if PJ_HAS_THREADS + PJ_ASSERT_RETURN(thread, PJ_EINVAL); + PJ_ASSERT_RETURN(prio>=THREAD_PRIORITY_IDLE && + prio<=THREAD_PRIORITY_TIME_CRITICAL, + PJ_EINVAL); + + if (SetThreadPriority(thread->hthread, prio) == FALSE) + return PJ_RETURN_OS_ERROR(GetLastError()); + + return PJ_SUCCESS; + +#else + PJ_UNUSED_ARG(thread); + PJ_UNUSED_ARG(prio); + pj_assert("pj_thread_set_prio() called in non-threading mode!"); + return PJ_EINVALIDOP; +#endif +} + + +/* + * Get the lowest priority value available on this system. + */ +PJ_DEF(int) pj_thread_get_prio_min(pj_thread_t *thread) +{ + PJ_UNUSED_ARG(thread); + return THREAD_PRIORITY_IDLE; +} + + +/* + * Get the highest priority value available on this system. + */ +PJ_DEF(int) pj_thread_get_prio_max(pj_thread_t *thread) +{ + PJ_UNUSED_ARG(thread); + return THREAD_PRIORITY_TIME_CRITICAL; +} + + +/* + * Get native thread handle + */ +PJ_DEF(void*) pj_thread_get_os_handle(pj_thread_t *thread) +{ + PJ_ASSERT_RETURN(thread, NULL); + +#if PJ_HAS_THREADS + return thread->hthread; +#else + pj_assert("pj_thread_is_registered() called in non-threading mode!"); + return NULL; +#endif +} + +/* + * pj_thread_register(..) + */ +PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, + pj_thread_desc desc, + pj_thread_t **thread_ptr) +{ + char stack_ptr; + pj_status_t rc; + pj_thread_t *thread = (pj_thread_t *)desc; + pj_str_t thread_name = pj_str((char*)cstr_thread_name); + + /* Size sanity check. */ + if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { + pj_assert(!"Not enough pj_thread_desc size!"); + return PJ_EBUG; + } + + /* If a thread descriptor has been registered before, just return it. */ + if (pj_thread_local_get (thread_tls_id) != 0) { + // 2006-02-26 bennylp: + // This wouldn't work in all cases!. + // If thread is created by external module (e.g. sound thread), + // thread may be reused while the pool used for the thread descriptor + // has been deleted by application. + //*thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id); + //return PJ_SUCCESS; + } + + /* Initialize and set the thread entry. */ + pj_bzero(desc, sizeof(struct pj_thread_t)); + thread->hthread = GetCurrentThread(); + thread->idthread = GetCurrentThreadId(); + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + thread->stk_start = &stack_ptr; + thread->stk_size = 0xFFFFFFFFUL; + thread->stk_max_usage = 0; +#else + stack_ptr = '\0'; +#endif + + if (cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) + pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), + cstr_thread_name, thread->idthread); + else + pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), + "thr%p", (void*)thread->idthread); + + rc = pj_thread_local_set(thread_tls_id, thread); + if (rc != PJ_SUCCESS) + return rc; + + *thread_ptr = thread; + return PJ_SUCCESS; +} + +/* + * pj_thread_init(void) + */ +pj_status_t pj_thread_init(void) +{ + pj_status_t rc; + pj_thread_t *thread; + + rc = pj_thread_local_alloc(&thread_tls_id); + if (rc != PJ_SUCCESS) + return rc; + + return pj_thread_register("thr%p", main_thread, &thread); +} + +static DWORD WINAPI thread_main(void *param) +{ + pj_thread_t *rec = param; + DWORD result; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + rec->stk_start = (char*)&rec; +#endif + + if (pj_thread_local_set(thread_tls_id, rec) != PJ_SUCCESS) { + pj_assert(!"TLS is not set (pj_init() error?)"); + } + + PJ_LOG(6,(rec->obj_name, "Thread started")); + + result = (*rec->proc)(rec->arg); + + PJ_LOG(6,(rec->obj_name, "Thread quitting")); +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + PJ_LOG(5,(rec->obj_name, "Thread stack max usage=%u by %s:%d", + rec->stk_max_usage, rec->caller_file, rec->caller_line)); +#endif + + return (DWORD)result; +} + +/* + * pj_thread_create(...) + */ +PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, + const char *thread_name, + pj_thread_proc *proc, + void *arg, + pj_size_t stack_size, + unsigned flags, + pj_thread_t **thread_ptr) +{ + DWORD dwflags = 0; + pj_thread_t *rec; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool && proc && thread_ptr, PJ_EINVAL); + + /* Set flags */ + if (flags & PJ_THREAD_SUSPENDED) + dwflags |= CREATE_SUSPENDED; + + /* Create thread record and assign name for the thread */ + rec = (struct pj_thread_t*) pj_pool_calloc(pool, 1, sizeof(pj_thread_t)); + if (!rec) + return PJ_ENOMEM; + + /* Set name. */ + if (!thread_name) + thread_name = "thr%p"; + + if (strchr(thread_name, '%')) { + pj_ansi_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec); + } else { + pj_ansi_strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME); + rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (rec->obj_name, "Thread created")); + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + rec->stk_size = stack_size ? stack_size : 0xFFFFFFFFUL; + rec->stk_max_usage = 0; +#endif + + /* Create the thread. */ + rec->proc = proc; + rec->arg = arg; + rec->hthread = CreateThread(NULL, stack_size, + thread_main, rec, + dwflags, &rec->idthread); + if (rec->hthread == NULL) + return PJ_RETURN_OS_ERROR(GetLastError()); + + /* Success! */ + *thread_ptr = rec; + return PJ_SUCCESS; +} + +/* + * pj_thread-get_name() + */ +PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p) +{ + pj_thread_t *rec = (pj_thread_t*)p; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, ""); + + return rec->obj_name; +} + +/* + * pj_thread_resume() + */ +PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) +{ + pj_thread_t *rec = (pj_thread_t*)p; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, PJ_EINVAL); + + if (ResumeThread(rec->hthread) == (DWORD)-1) + return PJ_RETURN_OS_ERROR(GetLastError()); + else + return PJ_SUCCESS; +} + +/* + * pj_thread_this() + */ +PJ_DEF(pj_thread_t*) pj_thread_this(void) +{ + pj_thread_t *rec = pj_thread_local_get(thread_tls_id); + + if (rec == NULL) { + pj_assert(!"Calling pjlib from unknown/external thread. You must " + "register external threads with pj_thread_register() " + "before calling any pjlib functions."); + } + + /* + * MUST NOT check stack because this function is called + * by PJ_CHECK_STACK() itself!!! + * + */ + + return rec; +} + +/* + * pj_thread_join() + */ +PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) +{ + pj_thread_t *rec = (pj_thread_t *)p; + DWORD rc; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, PJ_EINVAL); + + if (p == pj_thread_this()) + return PJ_ECANCELLED; + + PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name)); + + rc = WaitForSingleObject(rec->hthread, INFINITE); + + if (rc==WAIT_OBJECT_0) + return PJ_SUCCESS; + else if (rc==WAIT_TIMEOUT) + return PJ_ETIMEDOUT; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_thread_destroy() + */ +PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p) +{ + pj_thread_t *rec = (pj_thread_t *)p; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, PJ_EINVAL); + + if (CloseHandle(rec->hthread) == TRUE) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_thread_sleep() + */ +PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) +{ + PJ_CHECK_STACK(); + Sleep(msec); + return PJ_SUCCESS; +} + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0 +/* + * pj_thread_check_stack() + * Implementation for PJ_CHECK_STACK() + */ +PJ_DEF(void) pj_thread_check_stack(const char *file, int line) +{ + char stk_ptr; + pj_uint32_t usage; + pj_thread_t *thread = pj_thread_this(); + + pj_assert(thread); + + /* Calculate current usage. */ + usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start : + thread->stk_start - &stk_ptr; + + /* Assert if stack usage is dangerously high. */ + pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128)); + + /* Keep statistic. */ + if (usage > thread->stk_max_usage) { + thread->stk_max_usage = usage; + thread->caller_file = file; + thread->caller_line = line; + } + +} + +/* + * pj_thread_get_stack_max_usage() + */ +PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread) +{ + return thread->stk_max_usage; +} + +/* + * pj_thread_get_stack_info() + */ +PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread, + const char **file, + int *line ) +{ + pj_assert(thread); + + *file = thread->caller_file; + *line = thread->caller_line; + return 0; +} + +#endif /* PJ_OS_HAS_CHECK_STACK */ + + +/////////////////////////////////////////////////////////////////////////////// + +/* + * pj_atomic_create() + */ +PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, + pj_atomic_value_t initial, + pj_atomic_t **atomic_ptr) +{ + pj_atomic_t *atomic_var = pj_pool_alloc(pool, sizeof(pj_atomic_t)); + if (!atomic_var) + return PJ_ENOMEM; + + atomic_var->value = initial; + *atomic_ptr = atomic_var; + + return PJ_SUCCESS; +} + +/* + * pj_atomic_destroy() + */ +PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var ) +{ + PJ_UNUSED_ARG(var); + PJ_ASSERT_RETURN(var, PJ_EINVAL); + + return 0; +} + +/* + * pj_atomic_set() + */ +PJ_DEF(void) pj_atomic_set( pj_atomic_t *atomic_var, pj_atomic_value_t value) +{ + PJ_CHECK_STACK(); + + InterlockedExchange(&atomic_var->value, value); +} + +/* + * pj_atomic_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(atomic_var, 0); + + return atomic_var->value; +} + +/* + * pj_atomic_inc_and_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var) +{ + PJ_CHECK_STACK(); + +#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 + return InterlockedIncrement(&atomic_var->value); +#else + return InterlockedIncrement(&atomic_var->value); +#endif +} + +/* + * pj_atomic_inc() + */ +PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var) +{ + pj_atomic_inc_and_get(atomic_var); +} + +/* + * pj_atomic_dec_and_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var) +{ + PJ_CHECK_STACK(); + +#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 + return InterlockedDecrement(&atomic_var->value); +#else + return InterlockedDecrement(&atomic_var->value); +#endif +} + +/* + * pj_atomic_dec() + */ +PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var) +{ + pj_atomic_dec_and_get(atomic_var); +} + +/* + * pj_atomic_add() + */ +PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var, + pj_atomic_value_t value ) +{ +#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 + InterlockedExchangeAdd( &atomic_var->value, value ); +#else + InterlockedExchangeAdd( &atomic_var->value, value ); +#endif +} + +/* + * pj_atomic_add_and_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var, + pj_atomic_value_t value) +{ +#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 + long oldValue = InterlockedExchangeAdd( &atomic_var->value, value); + return oldValue + value; +#else + long oldValue = InterlockedExchangeAdd( &atomic_var->value, value); + return oldValue + value; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * pj_thread_local_alloc() + */ +PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index) +{ + PJ_ASSERT_RETURN(index != NULL, PJ_EINVAL); + + //Can't check stack because this function is called in the + //beginning before main thread is initialized. + //PJ_CHECK_STACK(); + + *index = TlsAlloc(); + + if (*index == TLS_OUT_OF_INDEXES) + return PJ_RETURN_OS_ERROR(GetLastError()); + else + return PJ_SUCCESS; +} + +/* + * pj_thread_local_free() + */ +PJ_DEF(void) pj_thread_local_free(long index) +{ + PJ_CHECK_STACK(); + TlsFree(index); +} + +/* + * pj_thread_local_set() + */ +PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value) +{ + BOOL rc; + + //Can't check stack because this function is called in the + //beginning before main thread is initialized. + //PJ_CHECK_STACK(); + rc = TlsSetValue(index, value); + return rc!=0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_thread_local_get() + */ +PJ_DEF(void*) pj_thread_local_get(long index) +{ + //Can't check stack because this function is called + //by PJ_CHECK_STACK() itself!!! + //PJ_CHECK_STACK(); + return TlsGetValue(index); +} + +/////////////////////////////////////////////////////////////////////////////// +static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name) +{ + + PJ_CHECK_STACK(); + +#if PJ_WIN32_WINNT >= 0x0400 + InitializeCriticalSection(&mutex->crit); +#else + mutex->hMutex = CreateMutex(NULL, FALSE, NULL); + if (!mutex->hMutex) { + return PJ_RETURN_OS_ERROR(GetLastError()); + } +#endif + +#if PJ_DEBUG + /* Set owner. */ + mutex->nesting_level = 0; + mutex->owner = NULL; +#endif + + /* Set name. */ + if (!name) { + name = "mtx%p"; + } + if (strchr(name, '%')) { + pj_ansi_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex); + } else { + pj_ansi_strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME); + mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (mutex->obj_name, "Mutex created")); + return PJ_SUCCESS; +} + +/* + * pj_mutex_create() + */ +PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool, + const char *name, + int type, + pj_mutex_t **mutex_ptr) +{ + pj_status_t rc; + pj_mutex_t *mutex; + + PJ_UNUSED_ARG(type); + PJ_ASSERT_RETURN(pool && mutex_ptr, PJ_EINVAL); + + mutex = pj_pool_alloc(pool, sizeof(*mutex)); + if (!mutex) + return PJ_ENOMEM; + + rc = init_mutex(mutex, name); + if (rc != PJ_SUCCESS) + return rc; + + *mutex_ptr = mutex; + + return PJ_SUCCESS; +} + +/* + * pj_mutex_create_simple() + */ +PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); +} + +/* + * pj_mutex_create_recursive() + */ +PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex); +} + +/* + * pj_mutex_lock() + */ +PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) +{ + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + LOG_MUTEX((mutex->obj_name, "Mutex: thread %s is waiting", + pj_thread_this()->obj_name)); + +#if PJ_WIN32_WINNT >= 0x0400 + EnterCriticalSection(&mutex->crit); + status=PJ_SUCCESS; +#else + if (WaitForSingleObject(mutex->hMutex, INFINITE)==WAIT_OBJECT_0) + status = PJ_SUCCESS; + else + status = PJ_STATUS_FROM_OS(GetLastError()); + +#endif + LOG_MUTEX((mutex->obj_name, + (status==PJ_SUCCESS ? "Mutex acquired by thread %s" : "FAILED by %s"), + pj_thread_this()->obj_name)); + +#if PJ_DEBUG + if (status == PJ_SUCCESS) { + mutex->owner = pj_thread_this(); + ++mutex->nesting_level; + } +#endif + + return status; +} + +/* + * pj_mutex_unlock() + */ +PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) +{ + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + +#if PJ_DEBUG + pj_assert(mutex->owner == pj_thread_this()); + if (--mutex->nesting_level == 0) { + mutex->owner = NULL; + } +#endif + + LOG_MUTEX((mutex->obj_name, "Mutex released by thread %s", + pj_thread_this()->obj_name)); + +#if PJ_WIN32_WINNT >= 0x0400 + LeaveCriticalSection(&mutex->crit); + status=PJ_SUCCESS; +#else + status = ReleaseMutex(mutex->hMutex) ? PJ_SUCCESS : + PJ_STATUS_FROM_OS(GetLastError()); +#endif + return status; +} + +/* + * pj_mutex_trylock() + */ +PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) +{ + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + LOG_MUTEX((mutex->obj_name, "Mutex: thread %s is trying", + pj_thread_this()->obj_name)); + +#if PJ_WIN32_WINNT >= 0x0400 + status=TryEnterCriticalSection(&mutex->crit) ? PJ_SUCCESS : PJ_EUNKNOWN; +#else + status = WaitForSingleObject(mutex->hMutex, 0)==WAIT_OBJECT_0 ? + PJ_SUCCESS : PJ_ETIMEDOUT; +#endif + if (status==PJ_SUCCESS) { + LOG_MUTEX((mutex->obj_name, "Mutex acquired by thread %s", + pj_thread_this()->obj_name)); + +#if PJ_DEBUG + mutex->owner = pj_thread_this(); + ++mutex->nesting_level; +#endif + } else { + LOG_MUTEX((mutex->obj_name, "Mutex: thread %s's trylock() failed", + pj_thread_this()->obj_name)); + } + + return status; +} + +/* + * pj_mutex_destroy() + */ +PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + LOG_MUTEX((mutex->obj_name, "Mutex destroyed")); + +#if PJ_WIN32_WINNT >= 0x0400 + DeleteCriticalSection(&mutex->crit); + return PJ_SUCCESS; +#else + return CloseHandle(mutex->hMutex) ? PJ_SUCCESS : + PJ_RETURN_OS_ERROR(GetLastError()); +#endif +} + +/* + * pj_mutex_is_locked() + */ +PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) +{ +#if PJ_DEBUG + return mutex->owner == pj_thread_this(); +#else + PJ_UNUSED_ARG(mutex); + pj_assert(!"PJ_DEBUG is not set!"); + return 1; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Win32 lacks Read/Write mutex, so include the emulation. + */ +#include "os_rwmutex.c" + +/////////////////////////////////////////////////////////////////////////////// +/* + * pj_enter_critical_section() + */ +PJ_DEF(void) pj_enter_critical_section(void) +{ + pj_mutex_lock(&critical_section_mutex); +} + + +/* + * pj_leave_critical_section() + */ +PJ_DEF(void) pj_leave_critical_section(void) +{ + pj_mutex_unlock(&critical_section_mutex); +} + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 + +/* + * pj_sem_create() + */ +PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_sem_t **sem_ptr) +{ + pj_sem_t *sem; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool && sem_ptr, PJ_EINVAL); + + sem = pj_pool_alloc(pool, sizeof(*sem)); + sem->hSemaphore = CreateSemaphore(NULL, initial, max, NULL); + if (!sem->hSemaphore) + return PJ_RETURN_OS_ERROR(GetLastError()); + + /* Set name. */ + if (!name) { + name = "sem%p"; + } + if (strchr(name, '%')) { + pj_ansi_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem); + } else { + pj_ansi_strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME); + sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + LOG_MUTEX((sem->obj_name, "Semaphore created")); + + *sem_ptr = sem; + return PJ_SUCCESS; +} + +static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout) +{ + DWORD result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + LOG_MUTEX((sem->obj_name, "Semaphore: thread %s is waiting", + pj_thread_this()->obj_name)); + + result = WaitForSingleObject(sem->hSemaphore, timeout); + if (result == WAIT_OBJECT_0) { + LOG_MUTEX((sem->obj_name, "Semaphore acquired by thread %s", + pj_thread_this()->obj_name)); + } else { + LOG_MUTEX((sem->obj_name, "Semaphore: thread %s FAILED to acquire", + pj_thread_this()->obj_name)); + } + + if (result==WAIT_OBJECT_0) + return PJ_SUCCESS; + else if (result==WAIT_TIMEOUT) + return PJ_ETIMEDOUT; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_sem_wait() + */ +PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + return pj_sem_wait_for(sem, INFINITE); +} + +/* + * pj_sem_trywait() + */ +PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + return pj_sem_wait_for(sem, 0); +} + +/* + * pj_sem_post() + */ +PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + LOG_MUTEX((sem->obj_name, "Semaphore released by thread %s", + pj_thread_this()->obj_name)); + + if (ReleaseSemaphore(sem->hSemaphore, 1, NULL)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_sem_destroy() + */ +PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + LOG_MUTEX((sem->obj_name, "Semaphore destroyed by thread %s", + pj_thread_this()->obj_name)); + + if (CloseHandle(sem->hSemaphore)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +#endif /* PJ_HAS_SEMAPHORE */ +/////////////////////////////////////////////////////////////////////////////// + + +#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 + +/* + * pj_event_create() + */ +PJ_DEF(pj_status_t) pj_event_create( pj_pool_t *pool, + const char *name, + pj_bool_t manual_reset, + pj_bool_t initial, + pj_event_t **event_ptr) +{ + pj_event_t *event; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool && event_ptr, PJ_EINVAL); + + event = pj_pool_alloc(pool, sizeof(*event)); + if (!event) + return PJ_ENOMEM; + + event->hEvent = CreateEvent(NULL, manual_reset?TRUE:FALSE, + initial?TRUE:FALSE, NULL); + + if (!event->hEvent) + return PJ_RETURN_OS_ERROR(GetLastError()); + + /* Set name. */ + if (!name) { + name = "evt%p"; + } + if (strchr(name, '%')) { + pj_ansi_snprintf(event->obj_name, PJ_MAX_OBJ_NAME, name, event); + } else { + pj_ansi_strncpy(event->obj_name, name, PJ_MAX_OBJ_NAME); + event->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (event->obj_name, "Event created")); + + *event_ptr = event; + return PJ_SUCCESS; +} + +static pj_status_t pj_event_wait_for(pj_event_t *event, unsigned timeout) +{ + DWORD result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + PJ_LOG(6, (event->obj_name, "Event: thread %s is waiting", + pj_thread_this()->obj_name)); + + result = WaitForSingleObject(event->hEvent, timeout); + if (result == WAIT_OBJECT_0) { + PJ_LOG(6, (event->obj_name, "Event: thread %s is released", + pj_thread_this()->obj_name)); + } else { + PJ_LOG(6, (event->obj_name, "Event: thread %s FAILED to acquire", + pj_thread_this()->obj_name)); + } + + if (result==WAIT_OBJECT_0) + return PJ_SUCCESS; + else if (result==WAIT_TIMEOUT) + return PJ_ETIMEDOUT; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_event_wait() + */ +PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event) +{ + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + return pj_event_wait_for(event, INFINITE); +} + +/* + * pj_event_trywait() + */ +PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event) +{ + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + return pj_event_wait_for(event, 0); +} + +/* + * pj_event_set() + */ +PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + PJ_LOG(6, (event->obj_name, "Setting event")); + + if (SetEvent(event->hEvent)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_event_pulse() + */ +PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + PJ_LOG(6, (event->obj_name, "Pulsing event")); + + if (PulseEvent(event->hEvent)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_event_reset() + */ +PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + PJ_LOG(6, (event->obj_name, "Event is reset")); + + if (ResetEvent(event->hEvent)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_event_destroy() + */ +PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + PJ_LOG(6, (event->obj_name, "Event is destroying")); + + if (CloseHandle(event->hEvent)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +#endif /* PJ_HAS_EVENT_OBJ */ + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 +/* + * Terminal color + */ + +static WORD pj_color_to_os_attr(pj_color_t color) +{ + WORD attr = 0; + + if (color & PJ_TERM_COLOR_R) + attr |= FOREGROUND_RED; + if (color & PJ_TERM_COLOR_G) + attr |= FOREGROUND_GREEN; + if (color & PJ_TERM_COLOR_B) + attr |= FOREGROUND_BLUE; + if (color & PJ_TERM_COLOR_BRIGHT) + attr |= FOREGROUND_INTENSITY; + + return attr; +} + +static pj_color_t os_attr_to_pj_color(WORD attr) +{ + int color = 0; + + if (attr & FOREGROUND_RED) + color |= PJ_TERM_COLOR_R; + if (attr & FOREGROUND_GREEN) + color |= PJ_TERM_COLOR_G; + if (attr & FOREGROUND_BLUE) + color |= PJ_TERM_COLOR_B; + if (attr & FOREGROUND_INTENSITY) + color |= PJ_TERM_COLOR_BRIGHT; + + return color; +} + + +/* + * pj_term_set_color() + */ +PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color) +{ + BOOL rc; + WORD attr = 0; + + PJ_CHECK_STACK(); + + attr = pj_color_to_os_attr(color); + rc = SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), attr); + return rc ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_term_get_color() + * Get current terminal foreground color. + */ +PJ_DEF(pj_color_t) pj_term_get_color(void) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + + PJ_CHECK_STACK(); + + GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &info); + return os_attr_to_pj_color(info.wAttributes); +} + +#endif /* PJ_TERM_HAS_COLOR */ + +/* + * pj_run_app() + */ +PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], + unsigned flags) +{ + PJ_UNUSED_ARG(flags); + return (*main_func)(argc, argv); +} diff --git a/pjlib/src/pj/os_error_linux_kernel.c b/pjlib/src/pj/os_error_linux_kernel.c new file mode 100644 index 0000000..bd2ac90 --- /dev/null +++ b/pjlib/src/pj/os_error_linux_kernel.c @@ -0,0 +1,81 @@ +/* $Id: os_error_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#if defined(MODVERSIONS) +#include +#endif +#include +#include + +int kernel_errno; + +PJ_DEF(pj_status_t) pj_get_os_error(void) +{ + return errno; +} + +PJ_DEF(void) pj_set_os_error(pj_status_t code) +{ + errno = code; +} + +PJ_DEF(pj_status_t) pj_get_netos_error(void) +{ + return errno; +} + +PJ_DEF(void) pj_set_netos_error(pj_status_t code) +{ + errno = code; +} + +/* + * platform_strerror() + * + * Platform specific error message. This file is called by pj_strerror() + * in errno.c + */ +int platform_strerror( pj_os_err_type os_errcode, + char *buf, pj_size_t bufsize) +{ + char errmsg[PJ_ERR_MSG_SIZE]; + int len; + + /* Handle EINVAL as special case so that it'll pass errno test. */ + if (os_errcode==EINVAL) + strcpy(errmsg, "Invalid value"); + else + snprintf(errmsg, sizeof(errmsg), "errno=%d", os_errcode); + + len = strlen(errmsg); + + if (len >= bufsize) + len = bufsize-1; + + pj_memcpy(buf, errmsg, len); + buf[len] = '\0'; + + return len; +} + + diff --git a/pjlib/src/pj/os_error_symbian.cpp b/pjlib/src/pj/os_error_symbian.cpp new file mode 100644 index 0000000..607754c --- /dev/null +++ b/pjlib/src/pj/os_error_symbian.cpp @@ -0,0 +1,172 @@ +/* $Id: os_error_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include + +#include +#include + + +#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING!=0) +static const struct { + pj_os_err_type code; + const char *msg; +} gaErrorList[] = { + /* + * Generic error -1 to -46 + */ + PJ_BUILD_ERR( KErrNotFound, "Unable to find the specified object"), + PJ_BUILD_ERR( KErrGeneral, "General (unspecified) error"), + PJ_BUILD_ERR( KErrCancel, "The operation was cancelled"), + PJ_BUILD_ERR( KErrNoMemory, "Not enough memory"), + PJ_BUILD_ERR( KErrNotSupported, "The operation requested is not supported"), + PJ_BUILD_ERR( KErrArgument, "Bad request"), + PJ_BUILD_ERR( KErrTotalLossOfPrecision, "Total loss of precision"), + PJ_BUILD_ERR( KErrBadHandle, "Bad object"), + PJ_BUILD_ERR( KErrOverflow, "Overflow"), + PJ_BUILD_ERR( KErrUnderflow, "Underflow"), + PJ_BUILD_ERR( KErrAlreadyExists,"Already exists"), + PJ_BUILD_ERR( KErrPathNotFound, "Unable to find the specified folder"), + PJ_BUILD_ERR( KErrDied, "Closed"), + PJ_BUILD_ERR( KErrInUse, "The specified object is currently in use by another program"), + PJ_BUILD_ERR( KErrServerTerminated, "Server has closed"), + PJ_BUILD_ERR( KErrServerBusy, "Server busy"), + PJ_BUILD_ERR( KErrCompletion, "Completion error"), + PJ_BUILD_ERR( KErrNotReady, "Not ready"), + PJ_BUILD_ERR( KErrUnknown, "Unknown error"), + PJ_BUILD_ERR( KErrCorrupt, "Corrupt"), + PJ_BUILD_ERR( KErrAccessDenied, "Access denied"), + PJ_BUILD_ERR( KErrLocked, "Locked"), + PJ_BUILD_ERR( KErrWrite, "Failed to write"), + PJ_BUILD_ERR( KErrDisMounted, "Wrong disk present"), + PJ_BUILD_ERR( KErrEof, "Unexpected end of file"), + PJ_BUILD_ERR( KErrDiskFull, "Disk full"), + PJ_BUILD_ERR( KErrBadDriver, "Bad device driver"), + PJ_BUILD_ERR( KErrBadName, "Bad name"), + PJ_BUILD_ERR( KErrCommsLineFail,"Comms line failed"), + PJ_BUILD_ERR( KErrCommsFrame, "Comms frame error"), + PJ_BUILD_ERR( KErrCommsOverrun, "Comms overrun error"), + PJ_BUILD_ERR( KErrCommsParity, "Comms parity error"), + PJ_BUILD_ERR( KErrTimedOut, "Timed out"), + PJ_BUILD_ERR( KErrCouldNotConnect, "Failed to connect"), + PJ_BUILD_ERR( KErrCouldNotDisconnect, "Failed to disconnect"), + PJ_BUILD_ERR( KErrDisconnected, "Disconnected"), + PJ_BUILD_ERR( KErrBadLibraryEntryPoint, "Bad library entry point"), + PJ_BUILD_ERR( KErrBadDescriptor,"Bad descriptor"), + PJ_BUILD_ERR( KErrAbort, "Interrupted"), + PJ_BUILD_ERR( KErrTooBig, "Too big"), + PJ_BUILD_ERR( KErrDivideByZero, "Divide by zero"), + PJ_BUILD_ERR( KErrBadPower, "Batteries too low"), + PJ_BUILD_ERR( KErrDirFull, "Folder full"), + PJ_BUILD_ERR( KErrHardwareNotAvailable, ""), + PJ_BUILD_ERR( KErrSessionClosed, ""), + PJ_BUILD_ERR( KErrPermissionDenied, ""), + + /* + * Socket errors (-190 - -1000) + */ + PJ_BUILD_ERR( KErrNetUnreach, "Could not connect to the network. Currently unreachable"), + PJ_BUILD_ERR( KErrHostUnreach, "Could not connect to the specified server"), + PJ_BUILD_ERR( KErrNoProtocolOpt,"The specified server refuses the selected protocol"), + PJ_BUILD_ERR( KErrUrgentData, ""), + PJ_BUILD_ERR( KErrWouldBlock, "Conflicts with KErrExtended, but cannot occur in practice"), + + {0, NULL} +}; + +#endif /* PJ_HAS_ERROR_STRING */ + + +PJ_DEF(pj_status_t) pj_get_os_error(void) +{ + return -1; +} + +PJ_DEF(void) pj_set_os_error(pj_status_t code) +{ + PJ_UNUSED_ARG(code); +} + +PJ_DEF(pj_status_t) pj_get_netos_error(void) +{ + return -1; +} + +PJ_DEF(void) pj_set_netos_error(pj_status_t code) +{ + PJ_UNUSED_ARG(code); +} + +PJ_BEGIN_DECL + + PJ_DECL(int) platform_strerror( pj_os_err_type os_errcode, + char *buf, pj_size_t bufsize); +PJ_END_DECL + +/* + * platform_strerror() + * + * Platform specific error message. This file is called by pj_strerror() + * in errno.c + */ +PJ_DEF(int) platform_strerror( pj_os_err_type os_errcode, + char *buf, pj_size_t bufsize) +{ + int len = 0; + + pj_assert(buf != NULL); + pj_assert(bufsize >= 0); + + /* + * MUST NOT check stack here. + * This function might be called from PJ_CHECK_STACK() itself! + //PJ_CHECK_STACK(); + */ + + if (!len) { +#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING!=0) + int i; + for (i = 0; gaErrorList[i].msg; ++i) { + if (gaErrorList[i].code == os_errcode) { + len = strlen(gaErrorList[i].msg); + if ((pj_size_t)len >= bufsize) { + len = bufsize-1; + } + pj_memcpy(buf, gaErrorList[i].msg, len); + buf[len] = '\0'; + break; + } + } +#endif /* PJ_HAS_ERROR_STRING */ + + } + + if (!len) { + len = pj_ansi_snprintf( buf, bufsize, "Symbian native error %d", + os_errcode); + buf[len] = '\0'; + } + + return len; +} + diff --git a/pjlib/src/pj/os_error_unix.c b/pjlib/src/pj/os_error_unix.c new file mode 100644 index 0000000..95c27b5 --- /dev/null +++ b/pjlib/src/pj/os_error_unix.c @@ -0,0 +1,69 @@ +/* $Id: os_error_unix.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include + +PJ_DEF(pj_status_t) pj_get_os_error(void) +{ + return PJ_STATUS_FROM_OS(errno); +} + +PJ_DEF(void) pj_set_os_error(pj_status_t code) +{ + errno = PJ_STATUS_TO_OS(code); +} + +PJ_DEF(pj_status_t) pj_get_netos_error(void) +{ + return PJ_STATUS_FROM_OS(errno); +} + +PJ_DEF(void) pj_set_netos_error(pj_status_t code) +{ + errno = PJ_STATUS_TO_OS(code); +} + +PJ_BEGIN_DECL + + PJ_DECL(int) platform_strerror(pj_os_err_type code, + char *buf, pj_size_t bufsize ); +PJ_END_DECL + +/* + * platform_strerror() + * + * Platform specific error message. This file is called by pj_strerror() + * in errno.c + */ +int platform_strerror( pj_os_err_type os_errcode, + char *buf, pj_size_t bufsize) +{ + const char *syserr = strerror(os_errcode); + pj_size_t len = syserr ? strlen(syserr) : 0; + + if (len >= bufsize) len = bufsize - 1; + if (len > 0) + pj_memcpy(buf, syserr, len); + buf[len] = '\0'; + return len; +} + + diff --git a/pjlib/src/pj/os_error_win32.c b/pjlib/src/pj/os_error_win32.c new file mode 100644 index 0000000..0e61b34 --- /dev/null +++ b/pjlib/src/pj/os_error_win32.c @@ -0,0 +1,220 @@ +/* $Id: os_error_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include + + +#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 +# include +#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 +# include +#endif + + +/* + * From Apache's APR: + */ +#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING!=0) + +static const struct { + pj_os_err_type code; + const char *msg; +} gaErrorList[] = { + PJ_BUILD_ERR( WSAEINTR, "Interrupted system call"), + PJ_BUILD_ERR( WSAEBADF, "Bad file number"), + PJ_BUILD_ERR( WSAEACCES, "Permission denied"), + PJ_BUILD_ERR( WSAEFAULT, "Bad address"), + PJ_BUILD_ERR( WSAEINVAL, "Invalid argument"), + PJ_BUILD_ERR( WSAEMFILE, "Too many open sockets"), + PJ_BUILD_ERR( WSAEWOULDBLOCK, "Operation would block"), + PJ_BUILD_ERR( WSAEINPROGRESS, "Operation now in progress"), + PJ_BUILD_ERR( WSAEALREADY, "Operation already in progress"), + PJ_BUILD_ERR( WSAENOTSOCK, "Socket operation on non-socket"), + PJ_BUILD_ERR( WSAEDESTADDRREQ, "Destination address required"), + PJ_BUILD_ERR( WSAEMSGSIZE, "Message too long"), + PJ_BUILD_ERR( WSAEPROTOTYPE, "Protocol wrong type for socket"), + PJ_BUILD_ERR( WSAENOPROTOOPT, "Bad protocol option"), + PJ_BUILD_ERR( WSAEPROTONOSUPPORT, "Protocol not supported"), + PJ_BUILD_ERR( WSAESOCKTNOSUPPORT, "Socket type not supported"), + PJ_BUILD_ERR( WSAEOPNOTSUPP, "Operation not supported on socket"), + PJ_BUILD_ERR( WSAEPFNOSUPPORT, "Protocol family not supported"), + PJ_BUILD_ERR( WSAEAFNOSUPPORT, "Address family not supported"), + PJ_BUILD_ERR( WSAEADDRINUSE, "Address already in use"), + PJ_BUILD_ERR( WSAEADDRNOTAVAIL, "Can't assign requested address"), + PJ_BUILD_ERR( WSAENETDOWN, "Network is down"), + PJ_BUILD_ERR( WSAENETUNREACH, "Network is unreachable"), + PJ_BUILD_ERR( WSAENETRESET, "Net connection reset"), + PJ_BUILD_ERR( WSAECONNABORTED, "Software caused connection abort"), + PJ_BUILD_ERR( WSAECONNRESET, "Connection reset by peer"), + PJ_BUILD_ERR( WSAENOBUFS, "No buffer space available"), + PJ_BUILD_ERR( WSAEISCONN, "Socket is already connected"), + PJ_BUILD_ERR( WSAENOTCONN, "Socket is not connected"), + PJ_BUILD_ERR( WSAESHUTDOWN, "Can't send after socket shutdown"), + PJ_BUILD_ERR( WSAETOOMANYREFS, "Too many references, can't splice"), + PJ_BUILD_ERR( WSAETIMEDOUT, "Connection timed out"), + PJ_BUILD_ERR( WSAECONNREFUSED, "Connection refused"), + PJ_BUILD_ERR( WSAELOOP, "Too many levels of symbolic links"), + PJ_BUILD_ERR( WSAENAMETOOLONG, "File name too long"), + PJ_BUILD_ERR( WSAEHOSTDOWN, "Host is down"), + PJ_BUILD_ERR( WSAEHOSTUNREACH, "No route to host"), + PJ_BUILD_ERR( WSAENOTEMPTY, "Directory not empty"), + PJ_BUILD_ERR( WSAEPROCLIM, "Too many processes"), + PJ_BUILD_ERR( WSAEUSERS, "Too many users"), + PJ_BUILD_ERR( WSAEDQUOT, "Disc quota exceeded"), + PJ_BUILD_ERR( WSAESTALE, "Stale NFS file handle"), + PJ_BUILD_ERR( WSAEREMOTE, "Too many levels of remote in path"), + PJ_BUILD_ERR( WSASYSNOTREADY, "Network system is unavailable"), + PJ_BUILD_ERR( WSAVERNOTSUPPORTED, "Winsock version out of range"), + PJ_BUILD_ERR( WSANOTINITIALISED, "WSAStartup not yet called"), + PJ_BUILD_ERR( WSAEDISCON, "Graceful shutdown in progress"), +/* +#define WSAENOMORE (WSABASEERR+102) +#define WSAECANCELLED (WSABASEERR+103) +#define WSAEINVALIDPROCTABLE (WSABASEERR+104) +#define WSAEINVALIDPROVIDER (WSABASEERR+105) +#define WSAEPROVIDERFAILEDINIT (WSABASEERR+106) +#define WSASYSCALLFAILURE (WSABASEERR+107) +#define WSASERVICE_NOT_FOUND (WSABASEERR+108) +#define WSATYPE_NOT_FOUND (WSABASEERR+109) +#define WSA_E_NO_MORE (WSABASEERR+110) +#define WSA_E_CANCELLED (WSABASEERR+111) +#define WSAEREFUSED (WSABASEERR+112) + */ + PJ_BUILD_ERR( WSAHOST_NOT_FOUND, "Host not found"), +/* +#define WSATRY_AGAIN (WSABASEERR+1002) +#define WSANO_RECOVERY (WSABASEERR+1003) + */ + PJ_BUILD_ERR( WSANO_DATA, "No host data of that type was found"), + {0, NULL} +}; + +#endif /* PJ_HAS_ERROR_STRING */ + + + +PJ_DEF(pj_status_t) pj_get_os_error(void) +{ + return PJ_STATUS_FROM_OS(GetLastError()); +} + +PJ_DEF(void) pj_set_os_error(pj_status_t code) +{ + SetLastError(PJ_STATUS_TO_OS(code)); +} + +PJ_DEF(pj_status_t) pj_get_netos_error(void) +{ + return PJ_STATUS_FROM_OS(WSAGetLastError()); +} + +PJ_DEF(void) pj_set_netos_error(pj_status_t code) +{ + WSASetLastError(PJ_STATUS_TO_OS(code)); +} + +/* + * platform_strerror() + * + * Platform specific error message. This file is called by pj_strerror() + * in errno.c + */ +int platform_strerror( pj_os_err_type os_errcode, + char *buf, pj_size_t bufsize) +{ + int len = 0; + PJ_DECL_UNICODE_TEMP_BUF(wbuf,128); + + pj_assert(buf != NULL); + pj_assert(bufsize >= 0); + + /* + * MUST NOT check stack here. + * This function might be called from PJ_CHECK_STACK() itself! + //PJ_CHECK_STACK(); + */ + + if (!len) { +#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING!=0) + int i; + for (i = 0; gaErrorList[i].msg; ++i) { + if (gaErrorList[i].code == os_errcode) { + len = strlen(gaErrorList[i].msg); + if ((pj_size_t)len >= bufsize) { + len = bufsize-1; + } + pj_memcpy(buf, gaErrorList[i].msg, len); + buf[len] = '\0'; + break; + } + } +#endif /* PJ_HAS_ERROR_STRING */ + + } + + + if (!len) { +#if PJ_NATIVE_STRING_IS_UNICODE + len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + os_errcode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + wbuf, + sizeof(wbuf), + NULL); + if (len) { + pj_unicode_to_ansi(wbuf, len, buf, bufsize); + } +#else + len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + os_errcode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buf, + bufsize, + NULL); + buf[bufsize-1] = '\0'; +#endif + + if (len) { + /* Remove trailing newlines. */ + while (len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { + buf[len-1] = '\0'; + --len; + } + } + } + + if (!len) { + len = pj_ansi_snprintf( buf, bufsize, "Win32 error code %u", + (unsigned)os_errcode); + if (len < 0 || len >= (int)bufsize) + len = bufsize-1; + buf[len] = '\0'; + } + + return len; +} + diff --git a/pjlib/src/pj/os_info.c b/pjlib/src/pj/os_info.c new file mode 100644 index 0000000..be67120 --- /dev/null +++ b/pjlib/src/pj/os_info.c @@ -0,0 +1,319 @@ +/* $Id: os_info.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include + +/* + * FYI these links contain useful infos about predefined macros across + * platforms: + * - http://predef.sourceforge.net/preos.html + */ + +#if defined(PJ_HAS_SYS_UTSNAME_H) && PJ_HAS_SYS_UTSNAME_H != 0 +/* For uname() */ +# include +# include +# define PJ_HAS_UNAME 1 +#endif + +#if defined(PJ_HAS_LIMITS_H) && PJ_HAS_LIMITS_H != 0 +/* Include to get to get various glibc macros. + * See http://predef.sourceforge.net/prelib.html + */ +# include +#endif + +#if defined(_MSC_VER) +/* For all Windows including mobile */ +# include +#endif + +#if defined(PJ_DARWINOS) && PJ_DARWINOS != 0 +# include "TargetConditionals.h" +#endif + +#ifndef PJ_SYS_INFO_BUFFER_SIZE +# define PJ_SYS_INFO_BUFFER_SIZE 64 +#endif + + +#if defined(PJ_DARWINOS) && PJ_DARWINOS != 0 && TARGET_OS_IPHONE + void pj_iphone_os_get_sys_info(pj_sys_info *si, pj_str_t *si_buffer); +#endif + +#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0 + PJ_BEGIN_DECL + unsigned pj_symbianos_get_model_info(char *buf, unsigned buf_size); + unsigned pj_symbianos_get_platform_info(char *buf, unsigned buf_size); + void pj_symbianos_get_sdk_info(pj_str_t *name, pj_uint32_t *ver); + PJ_END_DECL +#endif + + +static char *ver_info(pj_uint32_t ver, char *buf) +{ + int len; + + if (ver == 0) { + *buf = '\0'; + return buf; + } + + sprintf(buf, "-%u.%u", + (ver & 0xFF000000) >> 24, + (ver & 0x00FF0000) >> 16); + len = strlen(buf); + + if (ver & 0xFFFF) { + sprintf(buf+len, ".%u", (ver & 0xFF00) >> 8); + len = strlen(buf); + + if (ver & 0x00FF) { + sprintf(buf+len, ".%u", (ver & 0xFF)); + } + } + + return buf; +} + +static pj_uint32_t parse_version(char *str) +{ + char *tok; + int i, maxtok; + pj_uint32_t version = 0; + + while (*str && !pj_isdigit(*str)) + str++; + + maxtok = 4; + for (tok = strtok(str, ".-"), i=0; tok && i= len+1) { \ + si.field.ptr = si_buffer + PJ_SYS_INFO_BUFFER_SIZE - left; \ + si.field.slen = len; \ + pj_memcpy(si.field.ptr, str, len+1); \ + left -= (len+1); \ + } \ + } while (0) + + /* + * Machine and OS info. + */ +#if defined(PJ_HAS_UNAME) && PJ_HAS_UNAME + #if defined(PJ_DARWINOS) && PJ_DARWINOS != 0 && TARGET_OS_IPHONE && \ + (!defined TARGET_IPHONE_SIMULATOR || TARGET_IPHONE_SIMULATOR == 0) + { + pj_str_t buf = {si_buffer + PJ_SYS_INFO_BUFFER_SIZE - left, left}; + pj_str_t machine = {"arm", 3}; + pj_str_t sdk_name = {"iOS-SDK", 7}; + char tmp[PJ_SYS_INFO_BUFFER_SIZE]; + + pj_iphone_os_get_sys_info(&si, &buf); + left -= si.os_name.slen + 1; + + si.os_ver = parse_version(si.machine.ptr); + + si.machine = machine; + si.sdk_name = sdk_name; + + #ifdef PJ_SDK_NAME + pj_memcpy(tmp, PJ_SDK_NAME, pj_ansi_strlen(PJ_SDK_NAME) + 1); + si.sdk_ver = parse_version(tmp); + #endif + } + #else + { + struct utsname u; + + /* Successful uname() returns zero on Linux and positive value + * on OpenSolaris. + */ + if (uname(&u) == -1) + goto get_sdk_info; + + ALLOC_CP_STR(u.machine, machine); + ALLOC_CP_STR(u.sysname, os_name); + + si.os_ver = parse_version(u.release); + } + #endif +#elif defined(_MSC_VER) + { + OSVERSIONINFO ovi; + + ovi.dwOSVersionInfoSize = sizeof(ovi); + + if (GetVersionEx(&ovi) == FALSE) + goto get_sdk_info; + + si.os_ver = (ovi.dwMajorVersion << 24) | + (ovi.dwMinorVersion << 16); + #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE + si.os_name = pj_str("wince"); + #else + si.os_name = pj_str("win32"); + #endif + } + + { + SYSTEM_INFO wsi; + + GetSystemInfo(&wsi); + switch (wsi.wProcessorArchitecture) { + #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE + case PROCESSOR_ARCHITECTURE_ARM: + si.machine = pj_str("arm"); + break; + case PROCESSOR_ARCHITECTURE_SHX: + si.machine = pj_str("shx"); + break; + #else + case PROCESSOR_ARCHITECTURE_AMD64: + si.machine = pj_str("x86_64"); + break; + case PROCESSOR_ARCHITECTURE_IA64: + si.machine = pj_str("ia64"); + break; + case PROCESSOR_ARCHITECTURE_INTEL: + si.machine = pj_str("i386"); + break; + #endif /* PJ_WIN32_WINCE */ + } + } +#elif defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0 + { + pj_symbianos_get_model_info(si_buffer, sizeof(si_buffer)); + ALLOC_CP_STR(si_buffer, machine); + + char *p = si_buffer + sizeof(si_buffer) - left; + unsigned plen; + plen = pj_symbianos_get_platform_info(p, left); + if (plen) { + /* Output format will be "Series60vX.X" */ + si.os_name = pj_str("S60"); + si.os_ver = parse_version(p+9); + } else { + si.os_name = pj_str("Unknown"); + } + } +#endif + + /* + * SDK info. + */ +get_sdk_info: + +#if defined(__GLIBC__) + si.sdk_ver = (__GLIBC__ << 24) | + (__GLIBC_MINOR__ << 16); + si.sdk_name = pj_str("glibc"); +#elif defined(__GNU_LIBRARY__) + si.sdk_ver = (__GNU_LIBRARY__ << 24) | + (__GNU_LIBRARY_MINOR__ << 16); + si.sdk_name = pj_str("libc"); +#elif defined(__UCLIBC__) + si.sdk_ver = (__UCLIBC_MAJOR__ << 24) | + (__UCLIBC_MINOR__ << 16); + si.sdk_name = pj_str("uclibc"); +#elif defined(_WIN32_WCE) && _WIN32_WCE + /* Old window mobile declares _WIN32_WCE as decimal (e.g. 300, 420, etc.), + * but then it was changed to use hex, e.g. 0x420, etc. See + * http://social.msdn.microsoft.com/forums/en-US/vssmartdevicesnative/thread/8a97c59f-5a1c-4bc6-99e6-427f065ff439/ + */ + #if _WIN32_WCE <= 500 + si.sdk_ver = ( (_WIN32_WCE / 100) << 24) | + ( ((_WIN32_WCE % 100) / 10) << 16) | + ( (_WIN32_WCE % 10) << 8); + #else + si.sdk_ver = ( ((_WIN32_WCE & 0xFF00) >> 8) << 24) | + ( ((_WIN32_WCE & 0x00F0) >> 4) << 16) | + ( ((_WIN32_WCE & 0x000F) >> 0) << 8); + #endif + si.sdk_name = pj_str("cesdk"); +#elif defined(_MSC_VER) + /* No SDK info is easily obtainable for Visual C, so lets just use + * _MSC_VER. The _MSC_VER macro reports the major and minor versions + * of the compiler. For example, 1310 for Microsoft Visual C++ .NET 2003. + * 1310 represents version 13 and a 1.0 point release. + * The Visual C++ 2005 compiler version is 1400. + */ + si.sdk_ver = ((_MSC_VER / 100) << 24) | + (((_MSC_VER % 100) / 10) << 16) | + ((_MSC_VER % 10) << 8); + si.sdk_name = pj_str("msvc"); +#elif defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0 + pj_symbianos_get_sdk_info(&si.sdk_name, &si.sdk_ver); +#endif + + /* + * Build the info string. + */ + { + char tmp[PJ_SYS_INFO_BUFFER_SIZE]; + char os_ver[20], sdk_ver[20]; + int cnt; + + cnt = pj_ansi_snprintf(tmp, sizeof(tmp), + "%s%s%s%s%s%s%s", + si.os_name.ptr, + ver_info(si.os_ver, os_ver), + (si.machine.slen ? "/" : ""), + si.machine.ptr, + (si.sdk_name.slen ? "/" : ""), + si.sdk_name.ptr, + ver_info(si.sdk_ver, sdk_ver)); + if (cnt > 0 && cnt < (int)sizeof(tmp)) { + ALLOC_CP_STR(tmp, info); + } + } + + si_initialized = PJ_TRUE; + return &si; +} diff --git a/pjlib/src/pj/os_info_iphone.m b/pjlib/src/pj/os_info_iphone.m new file mode 100644 index 0000000..b7315c1 --- /dev/null +++ b/pjlib/src/pj/os_info_iphone.m @@ -0,0 +1,53 @@ +/* $Id: os_info_iphone.m 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 "TargetConditionals.h" + +#if !defined TARGET_IPHONE_SIMULATOR || TARGET_IPHONE_SIMULATOR == 0 + +#include +#include + +#include + +void pj_iphone_os_get_sys_info(pj_sys_info *si, pj_str_t *si_buffer) +{ + unsigned buf_len = si_buffer->slen, left = si_buffer->slen, len; + UIDevice *device = [UIDevice currentDevice]; + + if ([device respondsToSelector:@selector(isMultitaskingSupported)]) + si->flags |= PJ_SYS_HAS_IOS_BG; + +#define ALLOC_CP_STR(str,field) \ + do { \ + len = [str length]; \ + if (len && left >= len+1) { \ + si->field.ptr = si_buffer->ptr + buf_len - left; \ + si->field.slen = len; \ + [str getCString:si->field.ptr maxLength:len+1 \ + encoding:NSASCIIStringEncoding]; \ + left -= (len+1); \ + } \ + } while (0) + + ALLOC_CP_STR([device systemName], os_name); + ALLOC_CP_STR([device systemVersion], machine); +} + +#endif diff --git a/pjlib/src/pj/os_info_symbian.cpp b/pjlib/src/pj/os_info_symbian.cpp new file mode 100644 index 0000000..9389f04 --- /dev/null +++ b/pjlib/src/pj/os_info_symbian.cpp @@ -0,0 +1,190 @@ +/* $Id: os_info_symbian.cpp 3437 2011-03-08 06:30:34Z nanang $ */ +/* + * 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 + */ +#if !defined(PJ_SYMBIAN) || PJ_SYMBIAN == 0 +# error This file is only for Symbian platform +#endif + +#include +#include + +#include /* link against efsrv.lib */ +#include /* link against hal.lib */ +#include /* link against charconv.lib */ + + +PJ_BEGIN_DECL +unsigned pj_symbianos_get_model_info(char *buf, unsigned buf_size); +unsigned pj_symbianos_get_platform_info(char *buf, unsigned buf_size); +void pj_symbianos_get_sdk_info(pj_str_t *name, pj_uint32_t *ver); +PJ_END_DECL + + +/* Get Symbian phone model info, returning length of model info */ +unsigned pj_symbianos_get_model_info(char *buf, unsigned buf_size) +{ + pj_str_t model_name; + + /* Get machine UID */ + TInt hal_val; + HAL::Get(HAL::EMachineUid, hal_val); + pj_ansi_snprintf(buf, buf_size, "0x%08X", hal_val); + pj_strset2(&model_name, buf); + + /* Get model name */ + const pj_str_t st_copyright = {"(C)", 3}; + const pj_str_t st_nokia = {"Nokia", 5}; + char tmp_buf[64]; + pj_str_t tmp_str; + + _LIT(KModelFilename,"Z:\\resource\\versions\\model.txt"); + RFile file; + RFs fs; + TInt err; + + fs.Connect(1); + err = file.Open(fs, KModelFilename, EFileRead); + if (err == KErrNone) { + TFileText text; + text.Set(file); + TBuf16<64> ModelName16; + err = text.Read(ModelName16); + if (err == KErrNone) { + TPtr8 ptr8((TUint8*)tmp_buf, sizeof(tmp_buf)); + ptr8.Copy(ModelName16); + pj_strset(&tmp_str, tmp_buf, ptr8.Length()); + pj_strtrim(&tmp_str); + } + file.Close(); + } + fs.Close(); + if (err != KErrNone) + goto on_return; + + /* The retrieved model name is usually in long format, e.g: + * "© Nokia N95 (01.01)", "(C) Nokia E52". As we need only + * the short version, let's clean it up. + */ + + /* Remove preceding non-ASCII chars, e.g: "©" */ + char *p = tmp_str.ptr; + while (!pj_isascii(*p)) { p++; } + pj_strset(&tmp_str, p, tmp_str.slen - (p - tmp_str.ptr)); + + /* Remove "(C)" */ + p = pj_stristr(&tmp_str, &st_copyright); + if (p) { + p += st_copyright.slen; + pj_strset(&tmp_str, p, tmp_str.slen - (p - tmp_str.ptr)); + } + + /* Remove "Nokia" */ + p = pj_stristr(&tmp_str, &st_nokia); + if (p) { + p += st_nokia.slen; + pj_strset(&tmp_str, p, tmp_str.slen - (p - tmp_str.ptr)); + } + + /* Remove language version, e.g: "(01.01)" */ + p = pj_strchr(&tmp_str, '('); + if (p) { + tmp_str.slen = p - tmp_str.ptr; + } + + pj_strtrim(&tmp_str); + + if (tmp_str.slen == 0) + goto on_return; + + if ((unsigned)tmp_str.slen > buf_size - model_name.slen - 3) + tmp_str.slen = buf_size - model_name.slen - 3; + + pj_strcat2(&model_name, "("); + pj_strcat(&model_name, &tmp_str); + pj_strcat2(&model_name, ")"); + + /* Zero terminate */ + buf[model_name.slen] = '\0'; + +on_return: + return model_name.slen; +} + + +/* Get platform info, returned format will be "Series60vX.X" */ +unsigned pj_symbianos_get_platform_info(char *buf, unsigned buf_size) +{ + /* OS info */ + _LIT(KS60ProductIDFile, "Series60v*.sis"); + _LIT(KROMInstallDir, "z:\\system\\install\\"); + + RFs fs; + TFindFile ff(fs); + CDir* result; + pj_str_t plat_info = {NULL, 0}; + TInt err; + + fs.Connect(1); + err = ff.FindWildByDir(KS60ProductIDFile, KROMInstallDir, result); + if (err == KErrNone) { + err = result->Sort(ESortByName|EDescending); + if (err == KErrNone) { + TPtr8 tmp_ptr8((TUint8*)buf, buf_size); + const pj_str_t tmp_ext = {".sis", 4}; + char *p; + + tmp_ptr8.Copy((*result)[0].iName); + pj_strset(&plat_info, buf, (pj_size_t)tmp_ptr8.Length()); + p = pj_stristr(&plat_info, &tmp_ext); + if (p) + plat_info.slen -= (p - plat_info.ptr); + } + delete result; + } + fs.Close(); + buf[plat_info.slen] = '\0'; + + return plat_info.slen; +} + + +/* Get SDK info */ +void pj_symbianos_get_sdk_info(pj_str_t *name, pj_uint32_t *ver) +{ + const pj_str_t S60 = {"S60", 3}; + #if defined(__SERIES60_30__) + *name = S60; + *ver = (3 << 24); + #elif defined(__SERIES60_31__) + *name = S60; + *ver = (3 << 24) | (1 << 16); + #elif defined(__S60_32__) + *name = S60; + *ver = (3 << 24) | (2 << 16); + #elif defined(__S60_50__) + *name = S60; + *ver = (5 << 24); + #elif defined(__NOKIA_N97__) + *name = pj_str("N97"); + *ver = (1 << 24); + #else + *name = pj_str("Unknown"); + *ver = 0; + #endif +} + diff --git a/pjlib/src/pj/os_rwmutex.c b/pjlib/src/pj/os_rwmutex.c new file mode 100644 index 0000000..d392353 --- /dev/null +++ b/pjlib/src/pj/os_rwmutex.c @@ -0,0 +1,163 @@ +/* $Id: os_rwmutex.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + */ + +/* + * Note: + * + * DO NOT BUILD THIS FILE DIRECTLY. THIS FILE WILL BE INCLUDED BY os_core_*.c + * WHEN MACRO PJ_EMULATE_RWMUTEX IS SET. + */ + +/* + * os_rwmutex.c: + * + * Implementation of Read-Write mutex for platforms that lack it (e.g. + * Win32, RTEMS). + */ + + +struct pj_rwmutex_t +{ + pj_mutex_t *read_lock; + /* write_lock must use semaphore, because write_lock may be released + * by thread other than the thread that acquire the write_lock in the + * first place. + */ + pj_sem_t *write_lock; + pj_int32_t reader_count; +}; + +/* + * Create reader/writer mutex. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_create(pj_pool_t *pool, const char *name, + pj_rwmutex_t **p_mutex) +{ + pj_status_t status; + pj_rwmutex_t *rwmutex; + + PJ_ASSERT_RETURN(pool && p_mutex, PJ_EINVAL); + + *p_mutex = NULL; + rwmutex = PJ_POOL_ALLOC_T(pool, pj_rwmutex_t); + + status = pj_mutex_create_simple(pool, name, &rwmutex ->read_lock); + if (status != PJ_SUCCESS) + return status; + + status = pj_sem_create(pool, name, 1, 1, &rwmutex->write_lock); + if (status != PJ_SUCCESS) { + pj_mutex_destroy(rwmutex->read_lock); + return status; + } + + rwmutex->reader_count = 0; + *p_mutex = rwmutex; + return PJ_SUCCESS; +} + +/* + * Lock the mutex for reading. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_lock_read(pj_rwmutex_t *mutex) +{ + pj_status_t status; + + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + status = pj_mutex_lock(mutex->read_lock); + if (status != PJ_SUCCESS) { + pj_assert(!"This pretty much is unexpected"); + return status; + } + + mutex->reader_count++; + + pj_assert(mutex->reader_count < 0x7FFFFFF0L); + + if (mutex->reader_count == 1) + pj_sem_wait(mutex->write_lock); + + status = pj_mutex_unlock(mutex->read_lock); + return status; +} + +/* + * Lock the mutex for writing. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_lock_write(pj_rwmutex_t *mutex) +{ + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + return pj_sem_wait(mutex->write_lock); +} + +/* + * Release read lock. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_unlock_read(pj_rwmutex_t *mutex) +{ + pj_status_t status; + + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + status = pj_mutex_lock(mutex->read_lock); + if (status != PJ_SUCCESS) { + pj_assert(!"This pretty much is unexpected"); + return status; + } + + pj_assert(mutex->reader_count >= 1); + + --mutex->reader_count; + if (mutex->reader_count == 0) + pj_sem_post(mutex->write_lock); + + status = pj_mutex_unlock(mutex->read_lock); + return status; +} + +/* + * Release write lock. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_unlock_write(pj_rwmutex_t *mutex) +{ + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + pj_assert(mutex->reader_count <= 1); + return pj_sem_post(mutex->write_lock); +} + + +/* + * Destroy reader/writer mutex. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_destroy(pj_rwmutex_t *mutex) +{ + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + pj_mutex_destroy(mutex->read_lock); + pj_sem_destroy(mutex->write_lock); + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/os_symbian.h b/pjlib/src/pj/os_symbian.h new file mode 100644 index 0000000..9b3511b --- /dev/null +++ b/pjlib/src/pj/os_symbian.h @@ -0,0 +1,422 @@ +/* $Id: os_symbian.h 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 __OS_SYMBIAN_H__ +#define __OS_SYMBIAN_H__ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// Forward declarations +class CPjSocketReader; + +#ifndef PJ_SYMBIAN_TIMER_PRIORITY +# define PJ_SYMBIAN_TIMER_PRIORITY EPriorityNormal +#endif + +// +// PJLIB Symbian's Socket +// +class CPjSocket +{ +public: + enum + { + MAX_LEN = 1500, + }; + + // Construct CPjSocket + CPjSocket(int af, int sock_type, RSocket &sock) + : af_(af), sock_(sock), sock_type_(sock_type), connected_(false), + sockReader_(NULL) + { + } + + // Destroy CPjSocket + ~CPjSocket(); + + // Get address family + int GetAf() const + { + return af_; + } + + // Get the internal RSocket + RSocket& Socket() + { + return sock_; + } + + // Get socket connected flag. + bool IsConnected() const + { + return connected_; + } + + // Set socket connected flag. + void SetConnected(bool connected) + { + connected_ = connected; + } + + // Get socket type + int GetSockType() const + { + return sock_type_; + } + + // Returns true if socket is a datagram + bool IsDatagram() const + { + return sock_type_ == KSockDatagram; + } + + // Get socket reader, if any. + // May return NULL. + CPjSocketReader *Reader() + { + return sockReader_; + } + + // Create socket reader. + CPjSocketReader *CreateReader(unsigned max_len=CPjSocket::MAX_LEN); + + // Delete socket reader when it's not wanted. + void DestroyReader(); + +private: + int af_; + RSocket sock_; // Must not be reference, or otherwise + // it may point to local variable! + unsigned sock_type_; + + bool connected_; + CPjSocketReader *sockReader_; +}; + + +// +// Socket reader, used by select() and ioqueue abstraction +// +class CPjSocketReader : public CActive +{ +public: + // Construct. + static CPjSocketReader *NewL(CPjSocket &sock, unsigned max_len=CPjSocket::MAX_LEN); + + // Destroy; + ~CPjSocketReader(); + + // Start asynchronous read from the socket. + void StartRecv(void (*cb)(void *key)=NULL, + void *key=NULL, + TDes8 *aDesc = NULL, + TUint flags = 0); + + // Start asynchronous read from the socket. + void StartRecvFrom(void (*cb)(void *key)=NULL, + void *key=NULL, + TDes8 *aDesc = NULL, + TUint flags = 0, + TSockAddr *fromAddr = NULL); + + // Cancel asynchronous read. + void DoCancel(); + + // Implementation: called when read has completed. + void RunL(); + + // Check if there's pending data. + bool HasData() const + { + return buffer_.Length() != 0; + } + + // Append data to aDesc, up to aDesc's maximum size. + // If socket is datagram based, buffer_ will be clared. + void ReadData(TDes8 &aDesc, TInetAddr *addr=NULL); + +private: + CPjSocket &sock_; + bool isDatagram_; + TPtr8 buffer_; + TInetAddr recvAddr_; + + void (*readCb_)(void *key); + void *key_; + + // + // Constructor + // + CPjSocketReader(CPjSocket &sock); + void ConstructL(unsigned max_len); +}; + + + +// +// Time-out Timer Active Object +// +class CPjTimeoutTimer : public CActive +{ +public: + static CPjTimeoutTimer *NewL(); + ~CPjTimeoutTimer(); + + void StartTimer(TUint miliSeconds); + bool HasTimedOut() const; + +protected: + virtual void RunL(); + virtual void DoCancel(); + virtual TInt RunError(TInt aError); + +private: + RTimer timer_; + pj_bool_t hasTimedOut_; + + CPjTimeoutTimer(); + void ConstructL(); +}; + + + +// +// Symbian OS helper for PJLIB +// +class PjSymbianOS +{ +public: + // + // Get the singleton instance of PjSymbianOS + // + static PjSymbianOS *Instance(); + + // + // Set parameters + // + void SetParameters(pj_symbianos_params *params); + + // + // Initialize. + // + TInt Initialize(); + + // + // Shutdown. + // + void Shutdown(); + + + // + // Socket helper. + // + + // Get RSocketServ instance to be used by all sockets. + RSocketServ &SocketServ() + { + return appSocketServ_ ? *appSocketServ_ : socketServ_; + } + + // Get RConnection instance, if any. + RConnection *Connection() + { + return appConnection_; + } + + // Convert TInetAddr to pj_sockaddr_in + static inline pj_status_t Addr2pj(const TInetAddr & sym_addr, + pj_sockaddr &pj_addr, + int *addr_len, + pj_bool_t convert_ipv4_mapped_addr = PJ_FALSE) + { + TUint fam = sym_addr.Family(); + pj_bzero(&pj_addr, *addr_len); + if (fam == PJ_AF_INET || + (convert_ipv4_mapped_addr && + fam == PJ_AF_INET6 && + sym_addr.IsV4Mapped())) + { + pj_addr.addr.sa_family = PJ_AF_INET; + PJ_ASSERT_RETURN(*addr_len>=(int)sizeof(pj_sockaddr_in), PJ_ETOOSMALL); + pj_addr.ipv4.sin_addr.s_addr = pj_htonl(sym_addr.Address()); + pj_addr.ipv4.sin_port = pj_htons((pj_uint16_t) sym_addr.Port()); + *addr_len = sizeof(pj_sockaddr_in); + } else if (fam == PJ_AF_INET6) { + PJ_ASSERT_RETURN(*addr_len>=(int)sizeof(pj_sockaddr_in6), PJ_ETOOSMALL); + const TIp6Addr & ip6 = sym_addr.Ip6Address(); + pj_addr.addr.sa_family = PJ_AF_INET6; + pj_memcpy(&pj_addr.ipv6.sin6_addr, ip6.u.iAddr8, 16); + pj_addr.ipv6.sin6_port = pj_htons((pj_uint16_t) sym_addr.Port()); + pj_addr.ipv6.sin6_scope_id = pj_htonl(sym_addr.Scope()); + pj_addr.ipv6.sin6_flowinfo = pj_htonl(sym_addr.FlowLabel()); + *addr_len = sizeof(pj_sockaddr_in6); + } else { + pj_assert(!"Unsupported address family"); + return PJ_EAFNOTSUP; + } + + return PJ_SUCCESS; + } + + + // Convert pj_sockaddr_in to TInetAddr + static inline pj_status_t pj2Addr(const pj_sockaddr &pj_addr, + int addrlen, + TInetAddr & sym_addr) + { + if (pj_addr.addr.sa_family == PJ_AF_INET) { + PJ_ASSERT_RETURN(addrlen >= (int)sizeof(pj_sockaddr_in), PJ_EINVAL); + sym_addr.Init(KAfInet); + sym_addr.SetAddress((TUint32)pj_ntohl(pj_addr.ipv4.sin_addr.s_addr)); + sym_addr.SetPort(pj_ntohs(pj_addr.ipv4.sin_port)); + } else if (pj_addr.addr.sa_family == PJ_AF_INET6) { + TIp6Addr ip6; + + PJ_ASSERT_RETURN(addrlen>=(int)sizeof(pj_sockaddr_in6), PJ_EINVAL); + pj_memcpy(ip6.u.iAddr8, &pj_addr.ipv6.sin6_addr, 16); + sym_addr.Init(KAfInet6); + sym_addr.SetAddress(ip6); + sym_addr.SetScope(pj_ntohl(pj_addr.ipv6.sin6_scope_id)); + sym_addr.SetFlowLabel(pj_ntohl(pj_addr.ipv6.sin6_flowinfo)); + } else { + pj_assert(!"Unsupported address family"); + } + return PJ_SUCCESS; + } + + + // + // Resolver helper + // + + // Get RHostResolver instance + RHostResolver & GetResolver(int af) + { + if (af==PJ_AF_INET6) { + return appHostResolver6_ ? *appHostResolver6_ : hostResolver6_; + } else { + return appHostResolver_ ? *appHostResolver_ : hostResolver_; + } + } + + // + // Return true if the access point connection is up + // + bool IsConnectionUp() const + { + return isConnectionUp_; + } + + // + // Set access point connection status + // + void SetConnectionStatus(bool up) + { + isConnectionUp_ = up; + } + + // + // Unicode Converter + // + + // Convert to Unicode + TInt ConvertToUnicode(TDes16 &aUnicode, const TDesC8 &aForeign); + + // Convert from Unicode + TInt ConvertFromUnicode(TDes8 &aForeign, const TDesC16 &aUnicode); + + // + // Get console + // + + // Get console + CConsoleBase *Console() + { + return console_; + } + + // + // Get select() timeout timer. + // + CPjTimeoutTimer *SelectTimeoutTimer() + { + return selectTimeoutTimer_; + } + + // + // Wait for any active objects to run. + // + void WaitForActiveObjects(TInt aPriority = CActive::EPriorityStandard) + { + TInt aError; + CActiveScheduler::Current()->WaitForAnyRequest(); + CActiveScheduler::RunIfReady(aError, aPriority); + } + +private: + bool isConnectionUp_; + + bool isSocketServInitialized_; + RSocketServ socketServ_; + + bool isResolverInitialized_; + RHostResolver hostResolver_; + RHostResolver hostResolver6_; + + CConsoleBase* console_; + + CPjTimeoutTimer *selectTimeoutTimer_; + + // App parameters + RSocketServ *appSocketServ_; + RConnection *appConnection_; + RHostResolver *appHostResolver_; + RHostResolver *appHostResolver6_; + +private: + PjSymbianOS(); +}; + +// This macro is used to check the access point connection status and return +// failure if the AP connection is down or unusable. See the documentation +// of pj_symbianos_set_connection_status() for more info +#define PJ_SYMBIAN_CHECK_CONNECTION() \ + PJ_SYMBIAN_CHECK_CONNECTION2(PJ_ECANCELLED) + +#define PJ_SYMBIAN_CHECK_CONNECTION2(retval) \ + do { \ + if (!PjSymbianOS::Instance()->IsConnectionUp()) \ + return retval; \ + } while (0); + +#endif /* __OS_SYMBIAN_H__ */ + diff --git a/pjlib/src/pj/os_time_bsd.c b/pjlib/src/pj/os_time_bsd.c new file mode 100644 index 0000000..5240c9e --- /dev/null +++ b/pjlib/src/pj/os_time_bsd.c @@ -0,0 +1,36 @@ +/* $Id: os_time_bsd.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include + +/////////////////////////////////////////////////////////////////////////////// + +PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv) +{ + struct timeb tb; + + PJ_CHECK_STACK(); + + ftime(&tb); + tv->sec = tb.time; + tv->msec = tb.millitm; + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/os_time_common.c b/pjlib/src/pj/os_time_common.c new file mode 100644 index 0000000..cada701 --- /dev/null +++ b/pjlib/src/pj/os_time_common.c @@ -0,0 +1,86 @@ +/* $Id: os_time_common.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include + + +/////////////////////////////////////////////////////////////////////////////// + +PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt) +{ + struct tm *local_time; + + PJ_CHECK_STACK(); + + local_time = localtime((time_t*)&tv->sec); + + pt->year = local_time->tm_year+1900; + pt->mon = local_time->tm_mon; + pt->day = local_time->tm_mday; + pt->hour = local_time->tm_hour; + pt->min = local_time->tm_min; + pt->sec = local_time->tm_sec; + pt->wday = local_time->tm_wday; + pt->msec = tv->msec; + + return PJ_SUCCESS; +} + +/** + * Encode parsed time to time value. + */ +PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv) +{ + struct tm local_time; + + local_time.tm_year = pt->year-1900; + local_time.tm_mon = pt->mon; + local_time.tm_mday = pt->day; + local_time.tm_hour = pt->hour; + local_time.tm_min = pt->min; + local_time.tm_sec = pt->sec; + local_time.tm_isdst = 0; + + tv->sec = mktime(&local_time); + tv->msec = pt->msec; + + return PJ_SUCCESS; +} + +/** + * Convert local time to GMT. + */ +PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv) +{ + PJ_UNUSED_ARG(tv); + return PJ_EBUG; +} + +/** + * Convert GMT to local time. + */ +PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv) +{ + PJ_UNUSED_ARG(tv); + return PJ_EBUG; +} + + diff --git a/pjlib/src/pj/os_time_linux_kernel.c b/pjlib/src/pj/os_time_linux_kernel.c new file mode 100644 index 0000000..4c772a0 --- /dev/null +++ b/pjlib/src/pj/os_time_linux_kernel.c @@ -0,0 +1,66 @@ +/* $Id: os_time_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include + +/////////////////////////////////////////////////////////////////////////////// + +PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv) +{ + struct timeval tval; + + do_gettimeofday(&tval); + tv->sec = tval.tv_sec; + tv->msec = tval.tv_usec / 1000; + + return 0; +} + +PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt) +{ + pt->year = 2005; + pt->mon = 8; + pt->day = 20; + pt->hour = 16; + pt->min = 30; + pt->sec = 30; + pt->wday = 3; + pt->yday = 200; + pt->msec = 777; + + return -1; +} + +/** + * Encode parsed time to time value. + */ +PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv); + +/** + * Convert local time to GMT. + */ +PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv); + +/** + * Convert GMT to local time. + */ +PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv); + + diff --git a/pjlib/src/pj/os_time_unix.c b/pjlib/src/pj/os_time_unix.c new file mode 100644 index 0000000..4f3139b --- /dev/null +++ b/pjlib/src/pj/os_time_unix.c @@ -0,0 +1,47 @@ +/* $Id: os_time_unix.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include + +#if defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H!=0 +# include +#endif + +#include + +/////////////////////////////////////////////////////////////////////////////// + +PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *p_tv) +{ + struct timeval the_time; + int rc; + + PJ_CHECK_STACK(); + + rc = gettimeofday(&the_time, NULL); + if (rc != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); + + p_tv->sec = the_time.tv_sec; + p_tv->msec = the_time.tv_usec / 1000; + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/os_time_win32.c b/pjlib/src/pj/os_time_win32.c new file mode 100644 index 0000000..2143e2e --- /dev/null +++ b/pjlib/src/pj/os_time_win32.c @@ -0,0 +1,303 @@ +/* $Id: os_time_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// + +#define SECS_TO_FT_MULT 10000000 + +static LARGE_INTEGER base_time; + +#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE +# define WINCE_TIME +#endif + +#ifdef WINCE_TIME +/* Note: + * In Windows CE/Windows Mobile platforms, the availability of milliseconds + * time resolution in SYSTEMTIME.wMilliseconds depends on the OEM, and most + * likely it won't be available. When it's not available, the + * SYSTEMTIME.wMilliseconds will contain a constant arbitrary value. + * + * Because of that, we need to emulate the milliseconds time resolution + * using QueryPerformanceCounter() (via pj_get_timestamp() API). However + * there is limitation on using this, i.e. the time returned by + * pj_gettimeofday() may be off by up to plus/minus 999 msec (the second + * part will be correct, however the msec part may be off), because we're + * not synchronizing the msec field with the change of value of the "second" + * field of the system time. + * + * Also there is other caveat which need to be handled (and they are + * handled by this implementation): + * - user may change system time, so pj_gettimeofday() needs to periodically + * checks if system time has changed. The period on which system time is + * checked is controlled by PJ_WINCE_TIME_CHECK_INTERVAL macro. + */ +static LARGE_INTEGER g_start_time; /* Time gettimeofday() is first called */ +static pj_timestamp g_start_tick; /* TS gettimeofday() is first called */ +static pj_timestamp g_last_update; /* Last time check_system_time() is + called, to periodically synchronize + with up-to-date system time (in case + user changes system time). */ +static pj_uint64_t g_update_period; /* Period (in TS) check_system_time() + should be called. */ + +/* Period on which check_system_time() is called, in seconds */ +#ifndef PJ_WINCE_TIME_CHECK_INTERVAL +# define PJ_WINCE_TIME_CHECK_INTERVAL (10) +#endif + +#endif + +#ifdef WINCE_TIME +static pj_status_t init_start_time(void) +{ + SYSTEMTIME st; + FILETIME ft; + pj_timestamp freq; + pj_status_t status; + + GetLocalTime(&st); + SystemTimeToFileTime(&st, &ft); + + g_start_time.LowPart = ft.dwLowDateTime; + g_start_time.HighPart = ft.dwHighDateTime; + g_start_time.QuadPart /= SECS_TO_FT_MULT; + g_start_time.QuadPart -= base_time.QuadPart; + + status = pj_get_timestamp(&g_start_tick); + if (status != PJ_SUCCESS) + return status; + + g_last_update.u64 = g_start_tick.u64; + + status = pj_get_timestamp_freq(&freq); + if (status != PJ_SUCCESS) + return status; + + g_update_period = PJ_WINCE_TIME_CHECK_INTERVAL * freq.u64; + + PJ_LOG(4,("os_time_win32.c", "WinCE time (re)started")); + + return PJ_SUCCESS; +} + +static pj_status_t check_system_time(pj_uint64_t ts_elapsed) +{ + enum { MIS = 5 }; + SYSTEMTIME st; + FILETIME ft; + LARGE_INTEGER cur, calc; + DWORD diff; + pj_timestamp freq; + pj_status_t status; + + /* Get system's current time */ + GetLocalTime(&st); + SystemTimeToFileTime(&st, &ft); + + cur.LowPart = ft.dwLowDateTime; + cur.HighPart = ft.dwHighDateTime; + cur.QuadPart /= SECS_TO_FT_MULT; + cur.QuadPart -= base_time.QuadPart; + + /* Get our calculated system time */ + status = pj_get_timestamp_freq(&freq); + if (status != PJ_SUCCESS) + return status; + + calc.QuadPart = g_start_time.QuadPart + ts_elapsed / freq.u64; + + /* See the difference between calculated and actual system time */ + if (calc.QuadPart >= cur.QuadPart) { + diff = (DWORD)(calc.QuadPart - cur.QuadPart); + } else { + diff = (DWORD)(cur.QuadPart - calc.QuadPart); + } + + if (diff > MIS) { + /* System time has changed */ + PJ_LOG(3,("os_time_win32.c", "WinCE system time changed detected " + "(diff=%u)", diff)); + status = init_start_time(); + } else { + status = PJ_SUCCESS; + } + + return status; +} + +#endif + +// Find 1st Jan 1970 as a FILETIME +static pj_status_t get_base_time(void) +{ + SYSTEMTIME st; + FILETIME ft; + pj_status_t status = PJ_SUCCESS; + + memset(&st,0,sizeof(st)); + st.wYear=1970; + st.wMonth=1; + st.wDay=1; + SystemTimeToFileTime(&st, &ft); + + base_time.LowPart = ft.dwLowDateTime; + base_time.HighPart = ft.dwHighDateTime; + base_time.QuadPart /= SECS_TO_FT_MULT; + +#ifdef WINCE_TIME + pj_enter_critical_section(); + status = init_start_time(); + pj_leave_critical_section(); +#endif + + return status; +} + +PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv) +{ +#ifdef WINCE_TIME + pj_timestamp tick; + pj_uint64_t msec_elapsed; +#else + SYSTEMTIME st; + FILETIME ft; + LARGE_INTEGER li; +#endif + pj_status_t status; + + if (base_time.QuadPart == 0) { + status = get_base_time(); + if (status != PJ_SUCCESS) + return status; + } + +#ifdef WINCE_TIME + do { + status = pj_get_timestamp(&tick); + if (status != PJ_SUCCESS) + return status; + + if (tick.u64 - g_last_update.u64 >= g_update_period) { + pj_enter_critical_section(); + if (tick.u64 - g_last_update.u64 >= g_update_period) { + g_last_update.u64 = tick.u64; + check_system_time(tick.u64 - g_start_tick.u64); + } + pj_leave_critical_section(); + } else { + break; + } + } while (1); + + msec_elapsed = pj_elapsed_msec64(&g_start_tick, &tick); + + tv->sec = (long)(g_start_time.QuadPart + msec_elapsed/1000); + tv->msec = (long)(msec_elapsed % 1000); +#else + /* Standard Win32 GetLocalTime */ + GetLocalTime(&st); + SystemTimeToFileTime(&st, &ft); + + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + li.QuadPart /= SECS_TO_FT_MULT; + li.QuadPart -= base_time.QuadPart; + + tv->sec = li.LowPart; + tv->msec = st.wMilliseconds; +#endif /* WINCE_TIME */ + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt) +{ + LARGE_INTEGER li; + FILETIME ft; + SYSTEMTIME st; + + li.QuadPart = tv->sec; + li.QuadPart += base_time.QuadPart; + li.QuadPart *= SECS_TO_FT_MULT; + + ft.dwLowDateTime = li.LowPart; + ft.dwHighDateTime = li.HighPart; + FileTimeToSystemTime(&ft, &st); + + pt->year = st.wYear; + pt->mon = st.wMonth-1; + pt->day = st.wDay; + pt->wday = st.wDayOfWeek; + + pt->hour = st.wHour; + pt->min = st.wMinute; + pt->sec = st.wSecond; + pt->msec = tv->msec; + + return PJ_SUCCESS; +} + +/** + * Encode parsed time to time value. + */ +PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv) +{ + SYSTEMTIME st; + FILETIME ft; + LARGE_INTEGER li; + + pj_bzero(&st, sizeof(st)); + st.wYear = (pj_uint16_t) pt->year; + st.wMonth = (pj_uint16_t) (pt->mon + 1); + st.wDay = (pj_uint16_t) pt->day; + st.wHour = (pj_uint16_t) pt->hour; + st.wMinute = (pj_uint16_t) pt->min; + st.wSecond = (pj_uint16_t) pt->sec; + st.wMilliseconds = (pj_uint16_t) pt->msec; + + SystemTimeToFileTime(&st, &ft); + + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + li.QuadPart /= SECS_TO_FT_MULT; + li.QuadPart -= base_time.QuadPart; + + tv->sec = li.LowPart; + tv->msec = st.wMilliseconds; + + return PJ_SUCCESS; +} + +/** + * Convert local time to GMT. + */ +PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv); + +/** + * Convert GMT to local time. + */ +PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv); + + diff --git a/pjlib/src/pj/os_timestamp_common.c b/pjlib/src/pj/os_timestamp_common.c new file mode 100644 index 0000000..af5e493 --- /dev/null +++ b/pjlib/src/pj/os_timestamp_common.c @@ -0,0 +1,206 @@ +/* $Id: os_timestamp_common.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include + +#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 + +#define U32MAX (0xFFFFFFFFUL) +#define NANOSEC (1000000000UL) +#define USEC (1000000UL) +#define MSEC (1000) + +#define u64tohighprec(u64) ((pj_highprec_t)((pj_int64_t)(u64))) + +static pj_highprec_t get_elapsed( const pj_timestamp *start, + const pj_timestamp *stop ) +{ +#if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0 + return u64tohighprec(stop->u64 - start->u64); +#else + pj_highprec_t elapsed_hi, elapsed_lo; + + elapsed_hi = stop->u32.hi - start->u32.hi; + elapsed_lo = stop->u32.lo - start->u32.lo; + + /* elapsed_hi = elapsed_hi * U32MAX */ + pj_highprec_mul(elapsed_hi, U32MAX); + + return elapsed_hi + elapsed_lo; +#endif +} + +static pj_highprec_t elapsed_msec( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + pj_timestamp ts_freq; + pj_highprec_t freq, elapsed; + + if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS) + return 0; + + /* Convert frequency timestamp */ +#if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0 + freq = u64tohighprec(ts_freq.u64); +#else + freq = ts_freq.u32.hi; + pj_highprec_mul(freq, U32MAX); + freq += ts_freq.u32.lo; +#endif + + /* Avoid division by zero. */ + if (freq == 0) freq = 1; + + /* Get elapsed time in cycles. */ + elapsed = get_elapsed(start, stop); + + /* usec = elapsed * MSEC / freq */ + pj_highprec_mul(elapsed, MSEC); + pj_highprec_div(elapsed, freq); + + return elapsed; +} + +static pj_highprec_t elapsed_usec( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + pj_timestamp ts_freq; + pj_highprec_t freq, elapsed; + + if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS) + return 0; + + /* Convert frequency timestamp */ +#if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0 + freq = u64tohighprec(ts_freq.u64); +#else + freq = ts_freq.u32.hi; + pj_highprec_mul(freq, U32MAX); + freq += ts_freq.u32.lo; +#endif + + /* Avoid division by zero. */ + if (freq == 0) freq = 1; + + /* Get elapsed time in cycles. */ + elapsed = get_elapsed(start, stop); + + /* usec = elapsed * USEC / freq */ + pj_highprec_mul(elapsed, USEC); + pj_highprec_div(elapsed, freq); + + return elapsed; +} + +PJ_DEF(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + pj_timestamp ts_freq; + pj_highprec_t freq, elapsed; + + if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS) + return 0; + + /* Convert frequency timestamp */ +#if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0 + freq = u64tohighprec(ts_freq.u64); +#else + freq = ts_freq.u32.hi; + pj_highprec_mul(freq, U32MAX); + freq += ts_freq.u32.lo; +#endif + + /* Avoid division by zero. */ + if (freq == 0) freq = 1; + + /* Get elapsed time in cycles. */ + elapsed = get_elapsed(start, stop); + + /* usec = elapsed * USEC / freq */ + pj_highprec_mul(elapsed, NANOSEC); + pj_highprec_div(elapsed, freq); + + return (pj_uint32_t)elapsed; +} + +PJ_DEF(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + return (pj_uint32_t)elapsed_usec(start, stop); +} + +PJ_DEF(pj_uint32_t) pj_elapsed_msec( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + return (pj_uint32_t)elapsed_msec(start, stop); +} + +PJ_DEF(pj_uint64_t) pj_elapsed_msec64(const pj_timestamp *start, + const pj_timestamp *stop ) +{ + return (pj_uint64_t)elapsed_msec(start, stop); +} + +PJ_DEF(pj_time_val) pj_elapsed_time( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + pj_highprec_t elapsed = elapsed_msec(start, stop); + pj_time_val tv_elapsed; + + if (PJ_HIGHPREC_VALUE_IS_ZERO(elapsed)) { + tv_elapsed.sec = tv_elapsed.msec = 0; + return tv_elapsed; + } else { + pj_highprec_t sec, msec; + + sec = elapsed; + pj_highprec_div(sec, MSEC); + tv_elapsed.sec = (long)sec; + + msec = elapsed; + pj_highprec_mod(msec, MSEC); + tv_elapsed.msec = (long)msec; + + return tv_elapsed; + } +} + +PJ_DEF(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + return stop->u32.lo - start->u32.lo; +} + +PJ_DEF(pj_status_t) pj_gettickcount(pj_time_val *tv) +{ + pj_timestamp ts, start; + pj_status_t status; + + if ((status = pj_get_timestamp(&ts)) != PJ_SUCCESS) + return status; + + pj_set_timestamp32(&start, 0, 0); + *tv = pj_elapsed_time(&start, &ts); + + return PJ_SUCCESS; +} + +#endif /* PJ_HAS_HIGH_RES_TIMER */ + diff --git a/pjlib/src/pj/os_timestamp_linux_kernel.c b/pjlib/src/pj/os_timestamp_linux_kernel.c new file mode 100644 index 0000000..14609ea --- /dev/null +++ b/pjlib/src/pj/os_timestamp_linux_kernel.c @@ -0,0 +1,79 @@ +/* $Id: os_timestamp_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include + +#if 0 +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + ts->u32.hi = 0; + ts->u32.lo = jiffies; + return 0; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + freq->u32.hi = 0; + freq->u32.lo = HZ; + return 0; +} +#elif 0 +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + struct timespec tv; + + tv = CURRENT_TIME; + + ts->u64 = tv.tv_sec; + ts->u64 *= NSEC_PER_SEC; + ts->u64 += tv.tv_nsec; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + freq->u32.hi = 0; + freq->u32.lo = NSEC_PER_SEC; + return 0; +} +#else +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + struct timeval tv; + + do_gettimeofday(&tv); + + ts->u64 = tv.tv_sec; + ts->u64 *= USEC_PER_SEC; + ts->u64 += tv.tv_usec; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + freq->u32.hi = 0; + freq->u32.lo = USEC_PER_SEC; + return 0; +} + +#endif + diff --git a/pjlib/src/pj/os_timestamp_posix.c b/pjlib/src/pj/os_timestamp_posix.c new file mode 100644 index 0000000..215589c --- /dev/null +++ b/pjlib/src/pj/os_timestamp_posix.c @@ -0,0 +1,220 @@ +/* $Id: os_timestamp_posix.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include + +#if defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0 +# include + +# if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && \ + defined(_POSIX_MONOTONIC_CLOCK) +# define USE_POSIX_TIMERS 1 +# endif + +#endif + +#if defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM!=0 && \ + defined(PJ_TIMESTAMP_USE_RDTSC) && PJ_TIMESTAMP_USE_RDTSC!=0 && \ + defined(PJ_M_I386) && PJ_M_I386!=0 && \ + defined(PJ_LINUX) && PJ_LINUX!=0 +static int machine_speed_mhz; +static pj_timestamp machine_speed; + +static __inline__ unsigned long long int rdtsc() +{ + unsigned long long int x; + __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); + return x; +} + +/* Determine machine's CPU MHz to get the counter's frequency. + */ +static int get_machine_speed_mhz() +{ + FILE *strm; + char buf[512]; + int len; + char *pos, *end; + + PJ_CHECK_STACK(); + + /* Open /proc/cpuinfo and read the file */ + strm = fopen("/proc/cpuinfo", "r"); + if (!strm) + return -1; + len = fread(buf, 1, sizeof(buf), strm); + fclose(strm); + if (len < 1) { + return -1; + } + buf[len] = '\0'; + + /* Locate the MHz digit. */ + pos = strstr(buf, "cpu MHz"); + if (!pos) + return -1; + pos = strchr(pos, ':'); + if (!pos) + return -1; + end = (pos += 2); + while (isdigit(*end)) ++end; + *end = '\0'; + + /* Return the Mhz part, and give it a +1. */ + return atoi(pos)+1; +} + +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + if (machine_speed_mhz == 0) { + machine_speed_mhz = get_machine_speed_mhz(); + if (machine_speed_mhz > 0) { + machine_speed.u64 = machine_speed_mhz * 1000000.0; + } + } + + if (machine_speed_mhz == -1) { + ts->u64 = 0; + return -1; + } + ts->u64 = rdtsc(); + return 0; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + if (machine_speed_mhz == 0) { + machine_speed_mhz = get_machine_speed_mhz(); + if (machine_speed_mhz > 0) { + machine_speed.u64 = machine_speed_mhz * 1000000.0; + } + } + + if (machine_speed_mhz == -1) { + freq->u64 = 1; /* return 1 to prevent division by zero in apps. */ + return -1; + } + + freq->u64 = machine_speed.u64; + return 0; +} + +#elif defined(PJ_DARWINOS) && PJ_DARWINOS != 0 +#include +#include +#include + +#define NSEC_PER_SEC 1000000000 + +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + mach_timespec_t tp; + int ret; + clock_serv_t serv; + + ret = host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &serv); + if (ret != KERN_SUCCESS) { + return PJ_RETURN_OS_ERROR(EINVAL); + } + + ret = clock_get_time(serv, &tp); + if (ret != KERN_SUCCESS) { + return PJ_RETURN_OS_ERROR(EINVAL); + } + + ts->u64 = tp.tv_sec; + ts->u64 *= NSEC_PER_SEC; + ts->u64 += tp.tv_nsec; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + freq->u32.hi = 0; + freq->u32.lo = NSEC_PER_SEC; + + return PJ_SUCCESS; +} + +#elif defined(USE_POSIX_TIMERS) && USE_POSIX_TIMERS != 0 +#include +#include +#include + +#define NSEC_PER_SEC 1000000000 + +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + struct timespec tp; + + if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) { + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); + } + + ts->u64 = tp.tv_sec; + ts->u64 *= NSEC_PER_SEC; + ts->u64 += tp.tv_nsec; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + freq->u32.hi = 0; + freq->u32.lo = NSEC_PER_SEC; + + return PJ_SUCCESS; +} + +#else +#include +#include + +#define USEC_PER_SEC 1000000 + +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + struct timeval tv; + + if (gettimeofday(&tv, NULL) != 0) { + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); + } + + ts->u64 = tv.tv_sec; + ts->u64 *= USEC_PER_SEC; + ts->u64 += tv.tv_usec; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + freq->u32.hi = 0; + freq->u32.lo = USEC_PER_SEC; + + return PJ_SUCCESS; +} + +#endif diff --git a/pjlib/src/pj/os_timestamp_win32.c b/pjlib/src/pj/os_timestamp_win32.c new file mode 100644 index 0000000..7042ab1 --- /dev/null +++ b/pjlib/src/pj/os_timestamp_win32.c @@ -0,0 +1,295 @@ +/* $Id: os_timestamp_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include + +#define THIS_FILE "os_timestamp_win32.c" + + +#if 1 +# define TRACE_(x) PJ_LOG(3,x) +#else +# define TRACE_(x) ; +#endif + + +///////////////////////////////////////////////////////////////////////////// + +#if defined(PJ_TIMESTAMP_USE_RDTSC) && PJ_TIMESTAMP_USE_RDTSC!=0 && \ + defined(PJ_M_I386) && PJ_M_I386 != 0 && \ + defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM!=0 && \ + defined(_MSC_VER) + +/* + * Use rdtsc to get the OS timestamp. + */ +static LONG CpuMhz; +static pj_int64_t CpuHz; + +static pj_status_t GetCpuHz(void) +{ + HKEY key; + LONG rc; + DWORD size; + +#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 + rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", + 0, 0, &key); +#else + rc = RegOpenKey( HKEY_LOCAL_MACHINE, + "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", + &key); +#endif + + if (rc != ERROR_SUCCESS) + return PJ_RETURN_OS_ERROR(rc); + + size = sizeof(CpuMhz); + rc = RegQueryValueEx(key, "~MHz", NULL, NULL, (BYTE*)&CpuMhz, &size); + RegCloseKey(key); + + if (rc != ERROR_SUCCESS) { + return PJ_RETURN_OS_ERROR(rc); + } + + CpuHz = CpuMhz; + CpuHz = CpuHz * 1000000; + + return PJ_SUCCESS; +} + +/* __int64 is nicely returned in EDX:EAX */ +__declspec(naked) __int64 rdtsc() +{ + __asm + { + RDTSC + RET + } +} + +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + ts->u64 = rdtsc(); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + pj_status_t status; + + if (CpuHz == 0) { + status = GetCpuHz(); + if (status != PJ_SUCCESS) + return status; + } + + freq->u64 = CpuHz; + return PJ_SUCCESS; +} + +///////////////////////////////////////////////////////////////////////////// + +#elif defined(PJ_TIMESTAMP_WIN32_USE_SAFE_QPC) && \ + PJ_TIMESTAMP_WIN32_USE_SAFE_QPC!=0 + +/* Use safe QueryPerformanceCounter. + * This implementation has some protection against bug in KB Q274323: + * Performance counter value may unexpectedly leap forward + * http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323 + * + * THIS SHOULD NOT BE USED YET AS IT DOESN'T HANDLE SYSTEM TIME + * CHANGE. + */ + +static pj_timestamp g_ts_freq; +static pj_timestamp g_ts_base; +static pj_int64_t g_time_base; + +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + enum { MAX_RETRY = 10 }; + unsigned i; + + + /* pj_get_timestamp_freq() must have been called before. + * This is done when application called pj_init(). + */ + pj_assert(g_ts_freq.u64 != 0); + + /* Retry QueryPerformanceCounter() until we're sure that the + * value returned makes sense. + */ + i = 0; + do { + LARGE_INTEGER val; + pj_int64_t counter64, time64, diff; + pj_time_val time_now; + + /* Retrieve the counter */ + if (!QueryPerformanceCounter(&val)) + return PJ_RETURN_OS_ERROR(GetLastError()); + + /* Regardless of the goodness of the value, we should put + * the counter here, because normally application wouldn't + * check the error result of this function. + */ + ts->u64 = val.QuadPart; + + /* Retrieve time */ + pj_gettimeofday(&time_now); + + /* Get the counter elapsed time in miliseconds */ + counter64 = (val.QuadPart - g_ts_base.u64) * 1000 / g_ts_freq.u64; + + /* Get the time elapsed in miliseconds. + * We don't want to use PJ_TIME_VAL_MSEC() since it's using + * 32bit calculation, which limits the maximum elapsed time + * to around 49 days only. + */ + time64 = time_now.sec; + time64 = time64 * 1000 + time_now.msec; + //time64 = GetTickCount(); + + /* It's good if the difference between two clocks are within + * some compile time constant (default: 20ms, which to allow + * context switch happen between QueryPerformanceCounter and + * pj_gettimeofday()). + */ + diff = (time64 - g_time_base) - counter64; + if (diff >= -20 && diff <= 20) { + /* It's good */ + return PJ_SUCCESS; + } + + ++i; + + } while (i < MAX_RETRY); + + TRACE_((THIS_FILE, "QueryPerformanceCounter returned bad value")); + return PJ_ETIMEDOUT; +} + +static pj_status_t init_performance_counter(void) +{ + LARGE_INTEGER val; + pj_time_val time_base; + pj_status_t status; + + /* Get the frequency */ + if (!QueryPerformanceFrequency(&val)) + return PJ_RETURN_OS_ERROR(GetLastError()); + + g_ts_freq.u64 = val.QuadPart; + + /* Get the base timestamp */ + if (!QueryPerformanceCounter(&val)) + return PJ_RETURN_OS_ERROR(GetLastError()); + + g_ts_base.u64 = val.QuadPart; + + + /* Get the base time */ + status = pj_gettimeofday(&time_base); + if (status != PJ_SUCCESS) + return status; + + /* Convert time base to 64bit value in msec */ + g_time_base = time_base.sec; + g_time_base = g_time_base * 1000 + time_base.msec; + //g_time_base = GetTickCount(); + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + if (g_ts_freq.u64 == 0) { + enum { MAX_REPEAT = 10 }; + unsigned i; + pj_status_t status; + + /* Make unellegant compiler happy */ + status = 0; + + /* Repeat initializing performance counter until we're sure + * the base timing is correct. It is possible that the system + * returns bad counter during this initialization! + */ + for (i=0; iu64 = g_ts_freq.u64; + return PJ_SUCCESS; +} + +///////////////////////////////////////////////////////////////////////////// + +#else + +/* + * Use QueryPerformanceCounter and QueryPerformanceFrequency. + * This should be the default implementation to be used on Windows. + */ +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + LARGE_INTEGER val; + + if (!QueryPerformanceCounter(&val)) + return PJ_RETURN_OS_ERROR(GetLastError()); + + ts->u64 = val.QuadPart; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + LARGE_INTEGER val; + + if (!QueryPerformanceFrequency(&val)) + return PJ_RETURN_OS_ERROR(GetLastError()); + + freq->u64 = val.QuadPart; + return PJ_SUCCESS; +} + + +#endif /* PJ_TIMESTAMP_USE_RDTSC */ + diff --git a/pjlib/src/pj/pool.c b/pjlib/src/pj/pool.c new file mode 100644 index 0000000..9992df7 --- /dev/null +++ b/pjlib/src/pj/pool.c @@ -0,0 +1,301 @@ +/* $Id: pool.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include + +#if !PJ_HAS_POOL_ALT_API + + +/* Include inline definitions when inlining is disabled. */ +#if !PJ_FUNCTIONS_ARE_INLINED +# include +#endif + +#define LOG(expr) PJ_LOG(6,expr) +#define ALIGN_PTR(PTR,ALIGNMENT) (PTR + (-(long)(PTR) & (ALIGNMENT-1))) + +PJ_DEF_DATA(int) PJ_NO_MEMORY_EXCEPTION; + +PJ_DEF(int) pj_NO_MEMORY_EXCEPTION() +{ + return PJ_NO_MEMORY_EXCEPTION; +} + +/* + * Create new block. + * Create a new big chunk of memory block, from which user allocation will be + * taken from. + */ +static pj_pool_block *pj_pool_create_block( pj_pool_t *pool, pj_size_t size) +{ + pj_pool_block *block; + + PJ_CHECK_STACK(); + pj_assert(size >= sizeof(pj_pool_block)); + + LOG((pool->obj_name, "create_block(sz=%u), cur.cap=%u, cur.used=%u", + size, pool->capacity, pj_pool_get_used_size(pool))); + + /* Request memory from allocator. */ + block = (pj_pool_block*) + (*pool->factory->policy.block_alloc)(pool->factory, size); + if (block == NULL) { + (*pool->callback)(pool, size); + return NULL; + } + + /* Add capacity. */ + pool->capacity += size; + + /* Set start and end of buffer. */ + block->buf = ((unsigned char*)block) + sizeof(pj_pool_block); + block->end = ((unsigned char*)block) + size; + + /* Set the start pointer, aligning it as needed */ + block->cur = ALIGN_PTR(block->buf, PJ_POOL_ALIGNMENT); + + /* Insert in the front of the list. */ + pj_list_insert_after(&pool->block_list, block); + + LOG((pool->obj_name," block created, buffer=%p-%p",block->buf, block->end)); + + return block; +} + +/* + * Allocate memory chunk for user from available blocks. + * This will iterate through block list to find space to allocate the chunk. + * If no space is available in all the blocks, a new block might be created + * (depending on whether the pool is allowed to resize). + */ +PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size) +{ + pj_pool_block *block = pool->block_list.next; + void *p; + unsigned block_size; + + PJ_CHECK_STACK(); + + while (block != &pool->block_list) { + p = pj_pool_alloc_from_block(block, size); + if (p != NULL) + return p; + block = block->next; + } + /* No available space in all blocks. */ + + /* If pool is configured NOT to expand, return error. */ + if (pool->increment_size == 0) { + LOG((pool->obj_name, "Can't expand pool to allocate %u bytes " + "(used=%u, cap=%u)", + size, pj_pool_get_used_size(pool), pool->capacity)); + (*pool->callback)(pool, size); + return NULL; + } + + /* If pool is configured to expand, but the increment size + * is less than the required size, expand the pool by multiple + * increment size. Also count the size wasted due to aligning + * the block. + */ + if (pool->increment_size < + size + sizeof(pj_pool_block) + PJ_POOL_ALIGNMENT) + { + unsigned count; + count = (size + pool->increment_size + sizeof(pj_pool_block) + + PJ_POOL_ALIGNMENT) / + pool->increment_size; + block_size = count * pool->increment_size; + + } else { + block_size = pool->increment_size; + } + + LOG((pool->obj_name, + "%u bytes requested, resizing pool by %u bytes (used=%u, cap=%u)", + size, block_size, pj_pool_get_used_size(pool), pool->capacity)); + + block = pj_pool_create_block(pool, block_size); + if (!block) + return NULL; + + p = pj_pool_alloc_from_block(block, size); + pj_assert(p != NULL); +#if PJ_DEBUG + if (p == NULL) { + p = p; + } +#endif + return p; +} + +/* + * Internal function to initialize pool. + */ +PJ_DEF(void) pj_pool_init_int( pj_pool_t *pool, + const char *name, + pj_size_t increment_size, + pj_pool_callback *callback) +{ + PJ_CHECK_STACK(); + + pool->increment_size = increment_size; + pool->callback = callback; + + if (name) { + if (strchr(name, '%') != NULL) { + pj_ansi_snprintf(pool->obj_name, sizeof(pool->obj_name), + name, pool); + } else { + pj_ansi_strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME); + pool->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + } else { + pool->obj_name[0] = '\0'; + } +} + +/* + * Create new memory pool. + */ +PJ_DEF(pj_pool_t*) pj_pool_create_int( pj_pool_factory *f, const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback) +{ + pj_pool_t *pool; + pj_pool_block *block; + pj_uint8_t *buffer; + + PJ_CHECK_STACK(); + + /* Size must be at least sizeof(pj_pool)+sizeof(pj_pool_block) */ + PJ_ASSERT_RETURN(initial_size >= sizeof(pj_pool_t)+sizeof(pj_pool_block), + NULL); + + /* If callback is NULL, set calback from the policy */ + if (callback == NULL) + callback = f->policy.callback; + + /* Allocate initial block */ + buffer = (pj_uint8_t*) (*f->policy.block_alloc)(f, initial_size); + if (!buffer) + return NULL; + + /* Set pool administrative data. */ + pool = (pj_pool_t*)buffer; + pj_bzero(pool, sizeof(*pool)); + + pj_list_init(&pool->block_list); + pool->factory = f; + + /* Create the first block from the memory. */ + block = (pj_pool_block*) (buffer + sizeof(*pool)); + block->buf = ((unsigned char*)block) + sizeof(pj_pool_block); + block->end = buffer + initial_size; + + /* Set the start pointer, aligning it as needed */ + block->cur = ALIGN_PTR(block->buf, PJ_POOL_ALIGNMENT); + + pj_list_insert_after(&pool->block_list, block); + + pj_pool_init_int(pool, name, increment_size, callback); + + /* Pool initial capacity and used size */ + pool->capacity = initial_size; + + LOG((pool->obj_name, "pool created, size=%u", pool->capacity)); + return pool; +} + +/* + * Reset the pool to the state when it was created. + * All blocks will be deallocated except the first block. All memory areas + * are marked as free. + */ +static void reset_pool(pj_pool_t *pool) +{ + pj_pool_block *block; + + PJ_CHECK_STACK(); + + block = pool->block_list.prev; + if (block == &pool->block_list) + return; + + /* Skip the first block because it is occupying the same memory + as the pool itself. + */ + block = block->prev; + + while (block != &pool->block_list) { + pj_pool_block *prev = block->prev; + pj_list_erase(block); + (*pool->factory->policy.block_free)(pool->factory, block, + block->end - (unsigned char*)block); + block = prev; + } + + block = pool->block_list.next; + + /* Set the start pointer, aligning it as needed */ + block->cur = ALIGN_PTR(block->buf, PJ_POOL_ALIGNMENT); + + pool->capacity = block->end - (unsigned char*)pool; +} + +/* + * The public function to reset pool. + */ +PJ_DEF(void) pj_pool_reset(pj_pool_t *pool) +{ + LOG((pool->obj_name, "reset(): cap=%d, used=%d(%d%%)", + pool->capacity, pj_pool_get_used_size(pool), + pj_pool_get_used_size(pool)*100/pool->capacity)); + + reset_pool(pool); +} + +/* + * Destroy the pool. + */ +PJ_DEF(void) pj_pool_destroy_int(pj_pool_t *pool) +{ + pj_size_t initial_size; + + LOG((pool->obj_name, "destroy(): cap=%d, used=%d(%d%%), block0=%p-%p", + pool->capacity, pj_pool_get_used_size(pool), + pj_pool_get_used_size(pool)*100/pool->capacity, + ((pj_pool_block*)pool->block_list.next)->buf, + ((pj_pool_block*)pool->block_list.next)->end)); + + reset_pool(pool); + initial_size = ((pj_pool_block*)pool->block_list.next)->end - + (unsigned char*)pool; + if (pool->factory->policy.block_free) + (*pool->factory->policy.block_free)(pool->factory, pool, initial_size); +} + + +#endif /* PJ_HAS_POOL_ALT_API */ + diff --git a/pjlib/src/pj/pool_buf.c b/pjlib/src/pj/pool_buf.c new file mode 100644 index 0000000..b9044b2 --- /dev/null +++ b/pjlib/src/pj/pool_buf.c @@ -0,0 +1,115 @@ +/* $Id: pool_buf.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include + +struct pj_pool_factory stack_based_factory; + +struct creation_param +{ + void *stack_buf; + pj_size_t size; +}; + +static int is_initialized; +static long tls = -1; +static void* stack_alloc(pj_pool_factory *factory, pj_size_t size); + +static void pool_buf_cleanup(void) +{ + if (tls != -1) { + pj_thread_local_free(tls); + tls = -1; + } + if (is_initialized) + is_initialized = 0; +} + +static pj_status_t pool_buf_initialize() +{ + pj_atexit(&pool_buf_cleanup); + + stack_based_factory.policy.block_alloc = &stack_alloc; + return pj_thread_local_alloc(&tls); +} + +static void* stack_alloc(pj_pool_factory *factory, pj_size_t size) +{ + struct creation_param *param; + void *buf; + + PJ_UNUSED_ARG(factory); + + param = (struct creation_param*) pj_thread_local_get(tls); + if (param == NULL) { + /* Don't assert(), this is normal no-memory situation */ + return NULL; + } + + pj_thread_local_set(tls, NULL); + + PJ_ASSERT_RETURN(size <= param->size, NULL); + + buf = param->stack_buf; + + /* Prevent the buffer from being reused */ + param->stack_buf = NULL; + + return buf; +} + + +PJ_DEF(pj_pool_t*) pj_pool_create_on_buf(const char *name, + void *buf, + pj_size_t size) +{ +#if PJ_HAS_POOL_ALT_API == 0 + struct creation_param param; + long align_diff; + + PJ_ASSERT_RETURN(buf && size, NULL); + + if (!is_initialized) { + if (pool_buf_initialize() != PJ_SUCCESS) + return NULL; + is_initialized = 1; + } + + /* Check and align buffer */ + align_diff = (long)buf; + if (align_diff & (PJ_POOL_ALIGNMENT-1)) { + align_diff &= (PJ_POOL_ALIGNMENT-1); + buf = (void*) (((char*)buf) + align_diff); + size -= align_diff; + } + + param.stack_buf = buf; + param.size = size; + pj_thread_local_set(tls, ¶m); + + return pj_pool_create_int(&stack_based_factory, name, size, 0, + pj_pool_factory_default_policy.callback); +#else + PJ_UNUSED_ARG(buf); + return pj_pool_create(NULL, name, size, size, NULL); +#endif +} + diff --git a/pjlib/src/pj/pool_caching.c b/pjlib/src/pj/pool_caching.c new file mode 100644 index 0000000..a15c3d9 --- /dev/null +++ b/pjlib/src/pj/pool_caching.c @@ -0,0 +1,334 @@ +/* $Id: pool_caching.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include + +#if !PJ_HAS_POOL_ALT_API + +static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, + const char *name, + pj_size_t initial_size, + pj_size_t increment_sz, + pj_pool_callback *callback); +static void cpool_release_pool(pj_pool_factory *pf, pj_pool_t *pool); +static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail ); +static pj_bool_t cpool_on_block_alloc(pj_pool_factory *f, pj_size_t sz); +static void cpool_on_block_free(pj_pool_factory *f, pj_size_t sz); + + +static pj_size_t pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE] = +{ + 256, 512, 1024, 2048, 4096, 8192, 12288, 16384, + 20480, 24576, 28672, 32768, 40960, 49152, 57344, 65536 +}; + +/* Index where the search for size should begin. + * Start with pool_sizes[5], which is 8192. + */ +#define START_SIZE 5 + + +PJ_DEF(void) pj_caching_pool_init( pj_caching_pool *cp, + const pj_pool_factory_policy *policy, + pj_size_t max_capacity) +{ + int i; + pj_pool_t *pool; + + PJ_CHECK_STACK(); + + pj_bzero(cp, sizeof(*cp)); + + cp->max_capacity = max_capacity; + pj_list_init(&cp->used_list); + for (i=0; ifree_list[i]); + + if (policy == NULL) { + policy = &pj_pool_factory_default_policy; + } + + pj_memcpy(&cp->factory.policy, policy, sizeof(pj_pool_factory_policy)); + cp->factory.create_pool = &cpool_create_pool; + cp->factory.release_pool = &cpool_release_pool; + cp->factory.dump_status = &cpool_dump_status; + cp->factory.on_block_alloc = &cpool_on_block_alloc; + cp->factory.on_block_free = &cpool_on_block_free; + + pool = pj_pool_create_on_buf("cachingpool", cp->pool_buf, sizeof(cp->pool_buf)); + pj_lock_create_simple_mutex(pool, "cachingpool", &cp->lock); +} + +PJ_DEF(void) pj_caching_pool_destroy( pj_caching_pool *cp ) +{ + int i; + pj_pool_t *pool; + + PJ_CHECK_STACK(); + + /* Delete all pool in free list */ + for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE; ++i) { + pj_pool_t *pool = (pj_pool_t*) cp->free_list[i].next; + pj_pool_t *next; + for (; pool != (void*)&cp->free_list[i]; pool = next) { + next = pool->next; + pj_list_erase(pool); + pj_pool_destroy_int(pool); + } + } + + /* Delete all pools in used list */ + pool = (pj_pool_t*) cp->used_list.next; + while (pool != (pj_pool_t*) &cp->used_list) { + pj_pool_t *next = pool->next; + pj_list_erase(pool); + PJ_LOG(4,(pool->obj_name, + "Pool is not released by application, releasing now")); + pj_pool_destroy_int(pool); + pool = next; + } + + if (cp->lock) { + pj_lock_destroy(cp->lock); + pj_lock_create_null_mutex(NULL, "cachingpool", &cp->lock); + } +} + +static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, + const char *name, + pj_size_t initial_size, + pj_size_t increment_sz, + pj_pool_callback *callback) +{ + pj_caching_pool *cp = (pj_caching_pool*)pf; + pj_pool_t *pool; + int idx; + + PJ_CHECK_STACK(); + + pj_lock_acquire(cp->lock); + + /* Use pool factory's policy when callback is NULL */ + if (callback == NULL) { + callback = pf->policy.callback; + } + + /* Search the suitable size for the pool. + * We'll just do linear search to the size array, as the array size itself + * is only a few elements. Binary search I suspect will be less efficient + * for this purpose. + */ + if (initial_size <= pool_sizes[START_SIZE]) { + for (idx=START_SIZE-1; + idx >= 0 && pool_sizes[idx] >= initial_size; + --idx) + ; + ++idx; + } else { + for (idx=START_SIZE+1; + idx < PJ_CACHING_POOL_ARRAY_SIZE && + pool_sizes[idx] < initial_size; + ++idx) + ; + } + + /* Check whether there's a pool in the list. */ + if (idx==PJ_CACHING_POOL_ARRAY_SIZE || pj_list_empty(&cp->free_list[idx])) { + /* No pool is available. */ + /* Set minimum size. */ + if (idx < PJ_CACHING_POOL_ARRAY_SIZE) + initial_size = pool_sizes[idx]; + + /* Create new pool */ + pool = pj_pool_create_int(&cp->factory, name, initial_size, + increment_sz, callback); + if (!pool) { + pj_lock_release(cp->lock); + return NULL; + } + + } else { + /* Get one pool from the list. */ + pool = (pj_pool_t*) cp->free_list[idx].next; + pj_list_erase(pool); + + /* Initialize the pool. */ + pj_pool_init_int(pool, name, increment_sz, callback); + + /* Update pool manager's free capacity. */ + cp->capacity -= pj_pool_get_capacity(pool); + + PJ_LOG(6, (pool->obj_name, "pool reused, size=%u", pool->capacity)); + } + + /* Put in used list. */ + pj_list_insert_before( &cp->used_list, pool ); + + /* Mark factory data */ + pool->factory_data = (void*) (long) idx; + + /* Increment used count. */ + ++cp->used_count; + + pj_lock_release(cp->lock); + return pool; +} + +static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool) +{ + pj_caching_pool *cp = (pj_caching_pool*)pf; + unsigned pool_capacity; + unsigned i; + + PJ_CHECK_STACK(); + + PJ_ASSERT_ON_FAIL(pf && pool, return); + + pj_lock_acquire(cp->lock); + +#if PJ_SAFE_POOL + /* Make sure pool is still in our used list */ + if (pj_list_find_node(&cp->used_list, pool) != pool) { + pj_assert(!"Attempt to destroy pool that has been destroyed before"); + return; + } +#endif + + /* Erase from the used list. */ + pj_list_erase(pool); + + /* Decrement used count. */ + --cp->used_count; + + pool_capacity = pj_pool_get_capacity(pool); + + /* Destroy the pool if the size is greater than our size or if the total + * capacity in our recycle list (plus the size of the pool) exceeds + * maximum capacity. + . */ + if (pool_capacity > pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE-1] || + cp->capacity + pool_capacity > cp->max_capacity) + { + pj_pool_destroy_int(pool); + pj_lock_release(cp->lock); + return; + } + + /* Reset pool. */ + PJ_LOG(6, (pool->obj_name, "recycle(): cap=%d, used=%d(%d%%)", + pool_capacity, pj_pool_get_used_size(pool), + pj_pool_get_used_size(pool)*100/pool_capacity)); + pj_pool_reset(pool); + + pool_capacity = pj_pool_get_capacity(pool); + + /* + * Otherwise put the pool in our recycle list. + */ + i = (unsigned) (unsigned long) pool->factory_data; + + pj_assert(i= PJ_CACHING_POOL_ARRAY_SIZE ) { + /* Something has gone wrong with the pool. */ + pj_pool_destroy_int(pool); + pj_lock_release(cp->lock); + return; + } + + pj_list_insert_after(&cp->free_list[i], pool); + cp->capacity += pool_capacity; + + pj_lock_release(cp->lock); +} + +static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail ) +{ +#if PJ_LOG_MAX_LEVEL >= 3 + pj_caching_pool *cp = (pj_caching_pool*)factory; + + pj_lock_acquire(cp->lock); + + PJ_LOG(3,("cachpool", " Dumping caching pool:")); + PJ_LOG(3,("cachpool", " Capacity=%u, max_capacity=%u, used_cnt=%u", \ + cp->capacity, cp->max_capacity, cp->used_count)); + if (detail) { + pj_pool_t *pool = (pj_pool_t*) cp->used_list.next; + pj_uint32_t total_used = 0, total_capacity = 0; + PJ_LOG(3,("cachpool", " Dumping all active pools:")); + while (pool != (void*)&cp->used_list) { + unsigned pool_capacity = pj_pool_get_capacity(pool); + PJ_LOG(3,("cachpool", " %16s: %8d of %8d (%d%%) used", + pj_pool_getobjname(pool), + pj_pool_get_used_size(pool), + pool_capacity, + pj_pool_get_used_size(pool)*100/pool_capacity)); + total_used += pj_pool_get_used_size(pool); + total_capacity += pool_capacity; + pool = pool->next; + } + if (total_capacity) { + PJ_LOG(3,("cachpool", " Total %9d of %9d (%d %%) used!", + total_used, total_capacity, + total_used * 100 / total_capacity)); + } + } + + pj_lock_release(cp->lock); +#else + PJ_UNUSED_ARG(factory); + PJ_UNUSED_ARG(detail); +#endif +} + + +static pj_bool_t cpool_on_block_alloc(pj_pool_factory *f, pj_size_t sz) +{ + pj_caching_pool *cp = (pj_caching_pool*)f; + + //Can't lock because mutex is not recursive + //if (cp->mutex) pj_mutex_lock(cp->mutex); + + cp->used_size += sz; + if (cp->used_size > cp->peak_used_size) + cp->peak_used_size = cp->used_size; + + //if (cp->mutex) pj_mutex_unlock(cp->mutex); + + return PJ_TRUE; +} + + +static void cpool_on_block_free(pj_pool_factory *f, pj_size_t sz) +{ + pj_caching_pool *cp = (pj_caching_pool*)f; + + //pj_mutex_lock(cp->mutex); + cp->used_size -= sz; + //pj_mutex_unlock(cp->mutex); +} + + +#endif /* PJ_HAS_POOL_ALT_API */ + diff --git a/pjlib/src/pj/pool_dbg.c b/pjlib/src/pj/pool_dbg.c new file mode 100644 index 0000000..6d7bc77 --- /dev/null +++ b/pjlib/src/pj/pool_dbg.c @@ -0,0 +1,190 @@ +/* $Id: pool_dbg.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include + +#if PJ_HAS_POOL_ALT_API + +#if PJ_HAS_MALLOC_H +# include +#endif + + +#if PJ_HAS_STDLIB_H +# include +#endif + + +#if defined(PJ_WIN32) && PJ_WIN32!=0 && defined(PJ_DEBUG) && PJ_DEBUG!=0 \ + && !PJ_NATIVE_STRING_IS_UNICODE +# include +# define TRACE_(msg) OutputDebugString(msg) +#endif + +/* Uncomment this to enable TRACE_ */ +//#undef TRACE_ + + + +int PJ_NO_MEMORY_EXCEPTION; + + +PJ_DEF(int) pj_NO_MEMORY_EXCEPTION() +{ + return PJ_NO_MEMORY_EXCEPTION; +} + +/* Create pool */ +PJ_DEF(pj_pool_t*) pj_pool_create_imp( const char *file, int line, + void *factory, + const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback) +{ + pj_pool_t *pool; + + PJ_UNUSED_ARG(file); + PJ_UNUSED_ARG(line); + PJ_UNUSED_ARG(factory); + PJ_UNUSED_ARG(initial_size); + PJ_UNUSED_ARG(increment_size); + + pool = malloc(sizeof(struct pj_pool_t)); + if (!pool) + return NULL; + + if (name) { + pj_ansi_strncpy(pool->obj_name, name, sizeof(pool->obj_name)); + pool->obj_name[sizeof(pool->obj_name)-1] = '\0'; + } else { + strcpy(pool->obj_name, "altpool"); + } + + pool->factory = NULL; + pool->first_mem = NULL; + pool->used_size = 0; + pool->cb = callback; + + return pool; +} + + +/* Release pool */ +PJ_DEF(void) pj_pool_release_imp(pj_pool_t *pool) +{ + pj_pool_reset(pool); + free(pool); +} + +/* Get pool name */ +PJ_DEF(const char*) pj_pool_getobjname_imp(pj_pool_t *pool) +{ + PJ_UNUSED_ARG(pool); + return "pooldbg"; +} + +/* Reset pool */ +PJ_DEF(void) pj_pool_reset_imp(pj_pool_t *pool) +{ + struct pj_pool_mem *mem; + + mem = pool->first_mem; + while (mem) { + struct pj_pool_mem *next = mem->next; + free(mem); + mem = next; + } + + pool->first_mem = NULL; +} + +/* Get capacity */ +PJ_DEF(pj_size_t) pj_pool_get_capacity_imp(pj_pool_t *pool) +{ + PJ_UNUSED_ARG(pool); + + /* Unlimited capacity */ + return 0x7FFFFFFFUL; +} + +/* Get total used size */ +PJ_DEF(pj_size_t) pj_pool_get_used_size_imp(pj_pool_t *pool) +{ + return pool->used_size; +} + +/* Allocate memory from the pool */ +PJ_DEF(void*) pj_pool_alloc_imp( const char *file, int line, + pj_pool_t *pool, pj_size_t sz) +{ + struct pj_pool_mem *mem; + + PJ_UNUSED_ARG(file); + PJ_UNUSED_ARG(line); + + mem = malloc(sz + sizeof(struct pj_pool_mem)); + if (!mem) { + if (pool->cb) + (*pool->cb)(pool, sz); + return NULL; + } + + mem->next = pool->first_mem; + pool->first_mem = mem; + +#ifdef TRACE_ + { + char msg[120]; + pj_ansi_sprintf(msg, "Mem %X (%d+%d bytes) allocated by %s:%d\r\n", + mem, sz, sizeof(struct pj_pool_mem), + file, line); + TRACE_(msg); + } +#endif + + return ((char*)mem) + sizeof(struct pj_pool_mem); +} + +/* Allocate memory from the pool and zero the memory */ +PJ_DEF(void*) pj_pool_calloc_imp( const char *file, int line, + pj_pool_t *pool, unsigned cnt, + unsigned elemsz) +{ + void *mem; + + mem = pj_pool_alloc_imp(file, line, pool, cnt*elemsz); + if (!mem) + return NULL; + + pj_bzero(mem, cnt*elemsz); + return mem; +} + +/* Allocate memory from the pool and zero the memory */ +PJ_DEF(void*) pj_pool_zalloc_imp( const char *file, int line, + pj_pool_t *pool, pj_size_t sz) +{ + return pj_pool_calloc_imp(file, line, pool, 1, sz); +} + + + +#endif /* PJ_HAS_POOL_ALT_API */ diff --git a/pjlib/src/pj/pool_policy_kmalloc.c b/pjlib/src/pj/pool_policy_kmalloc.c new file mode 100644 index 0000000..2b5ec70 --- /dev/null +++ b/pjlib/src/pj/pool_policy_kmalloc.c @@ -0,0 +1,64 @@ +/* $Id: pool_policy_kmalloc.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include + + +static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(factory); + + return kmalloc(size, GFP_ATOMIC); +} + +static void default_block_free(pj_pool_factory *factory, + void *mem, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(factory); + PJ_UNUSED_ARG(size); + + kfree(mem); +} + +static void default_pool_callback(pj_pool_t *pool, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(size); + + PJ_THROW(PJ_NO_MEMORY_EXCEPTION); +} + +pj_pool_factory_policy pj_pool_factory_default_policy = +{ + &default_block_alloc, + &default_block_free, + &default_pool_callback, + 0 +}; + +PJ_DEF(const pj_pool_factory_policy*) pj_pool_factory_get_default_policy(void) +{ + return &pj_pool_factory_default_policy; +} + diff --git a/pjlib/src/pj/pool_policy_malloc.c b/pjlib/src/pj/pool_policy_malloc.c new file mode 100644 index 0000000..6bba91d --- /dev/null +++ b/pjlib/src/pj/pool_policy_malloc.c @@ -0,0 +1,104 @@ +/* $Id: pool_policy_malloc.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include + +#if !PJ_HAS_POOL_ALT_API + +/* + * This file contains pool default policy definition and implementation. + */ +#include "pool_signature.h" + + +static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size) +{ + void *p; + + PJ_CHECK_STACK(); + + if (factory->on_block_alloc) { + int rc; + rc = factory->on_block_alloc(factory, size); + if (!rc) + return NULL; + } + + p = malloc(size+(SIG_SIZE << 1)); + + if (p == NULL) { + if (factory->on_block_free) + factory->on_block_free(factory, size); + } else { + /* Apply signature when PJ_SAFE_POOL is set. It will move + * "p" pointer forward. + */ + APPLY_SIG(p, size); + } + + return p; +} + +static void default_block_free(pj_pool_factory *factory, void *mem, + pj_size_t size) +{ + PJ_CHECK_STACK(); + + if (factory->on_block_free) + factory->on_block_free(factory, size); + + /* Check and remove signature when PJ_SAFE_POOL is set. It will + * move "mem" pointer backward. + */ + REMOVE_SIG(mem, size); + + /* Note that when PJ_SAFE_POOL is set, the actual size of the block + * is size + SIG_SIZE*2. + */ + + free(mem); +} + +static void default_pool_callback(pj_pool_t *pool, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(size); + + PJ_THROW(PJ_NO_MEMORY_EXCEPTION); +} + +PJ_DEF_DATA(pj_pool_factory_policy) pj_pool_factory_default_policy = +{ + &default_block_alloc, + &default_block_free, + &default_pool_callback, + 0 +}; + +PJ_DEF(const pj_pool_factory_policy*) pj_pool_factory_get_default_policy(void) +{ + return &pj_pool_factory_default_policy; +} + + +#endif /* PJ_HAS_POOL_ALT_API */ diff --git a/pjlib/src/pj/pool_policy_new.cpp b/pjlib/src/pj/pool_policy_new.cpp new file mode 100644 index 0000000..909eb9f --- /dev/null +++ b/pjlib/src/pj/pool_policy_new.cpp @@ -0,0 +1,102 @@ +/* $Id: pool_policy_new.cpp 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include + +#if !PJ_HAS_POOL_ALT_API + +/* + * This file contains pool default policy definition and implementation. + */ +#include "pool_signature.h" + + +static void *operator_new(pj_pool_factory *factory, pj_size_t size) +{ + void *mem; + + PJ_CHECK_STACK(); + + if (factory->on_block_alloc) { + int rc; + rc = factory->on_block_alloc(factory, size); + if (!rc) + return NULL; + } + + mem = (void*) new char[size+(SIG_SIZE << 1)]; + + /* Exception for new operator may be disabled, so.. */ + if (mem) { + /* Apply signature when PJ_SAFE_POOL is set. It will move + * "mem" pointer forward. + */ + APPLY_SIG(mem, size); + } + + return mem; +} + +static void operator_delete(pj_pool_factory *factory, void *mem, pj_size_t size) +{ + PJ_CHECK_STACK(); + + if (factory->on_block_free) + factory->on_block_free(factory, size); + + /* Check and remove signature when PJ_SAFE_POOL is set. It will + * move "mem" pointer backward. + */ + REMOVE_SIG(mem, size); + + /* Note that when PJ_SAFE_POOL is set, the actual size of the block + * is size + SIG_SIZE*2. + */ + + char *p = (char*)mem; + delete [] p; +} + +static void default_pool_callback(pj_pool_t *pool, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(size); + + PJ_THROW(PJ_NO_MEMORY_EXCEPTION); +} + +PJ_DEF_DATA(pj_pool_factory_policy) pj_pool_factory_default_policy = +{ + &operator_new, + &operator_delete, + &default_pool_callback, + 0 +}; + +PJ_DEF(const pj_pool_factory_policy*) pj_pool_factory_get_default_policy(void) +{ + return &pj_pool_factory_default_policy; +} + + +#endif /* PJ_HAS_POOL_ALT_API */ + diff --git a/pjlib/src/pj/pool_signature.h b/pjlib/src/pj/pool_signature.h new file mode 100644 index 0000000..6d0d432 --- /dev/null +++ b/pjlib/src/pj/pool_signature.h @@ -0,0 +1,68 @@ +/* $Id: pool_signature.h 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include + +#if PJ_SAFE_POOL +# define SIG_SIZE sizeof(pj_uint32_t) + +static void apply_signature(void *p, pj_size_t size); +static void check_pool_signature(void *p, pj_size_t size); + +# define APPLY_SIG(p,sz) apply_signature(p,sz), \ + p=(void*)(((char*)p)+SIG_SIZE) +# define REMOVE_SIG(p,sz) check_pool_signature(p,sz), \ + p=(void*)(((char*)p)-SIG_SIZE) + +# define SIG_BEGIN 0x600DC0DE +# define SIG_END 0x0BADC0DE + +static void apply_signature(void *p, pj_size_t size) +{ + pj_uint32_t sig; + + sig = SIG_BEGIN; + pj_memcpy(p, &sig, SIG_SIZE); + + sig = SIG_END; + pj_memcpy(((char*)p)+SIG_SIZE+size, &sig, SIG_SIZE); +} + +static void check_pool_signature(void *p, pj_size_t size) +{ + pj_uint32_t sig; + pj_uint8_t *mem = (pj_uint8_t*)p; + + /* Check that signature at the start of the block is still intact */ + sig = SIG_BEGIN; + pj_assert(!pj_memcmp(mem-SIG_SIZE, &sig, SIG_SIZE)); + + /* Check that signature at the end of the block is still intact. + * Note that "mem" has been incremented by SIG_SIZE + */ + sig = SIG_END; + pj_assert(!pj_memcmp(mem+size, &sig, SIG_SIZE)); +} + +#else +# define SIG_SIZE 0 +# define APPLY_SIG(p,sz) +# define REMOVE_SIG(p,sz) +#endif diff --git a/pjlib/src/pj/rand.c b/pjlib/src/pj/rand.c new file mode 100644 index 0000000..59bad5f --- /dev/null +++ b/pjlib/src/pj/rand.c @@ -0,0 +1,35 @@ +/* $Id: rand.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include + +PJ_DEF(void) pj_srand(unsigned int seed) +{ + PJ_CHECK_STACK(); + platform_srand(seed); +} + +PJ_DEF(int) pj_rand(void) +{ + PJ_CHECK_STACK(); + return platform_rand(); +} + diff --git a/pjlib/src/pj/rbtree.c b/pjlib/src/pj/rbtree.c new file mode 100644 index 0000000..37c977e --- /dev/null +++ b/pjlib/src/pj/rbtree.c @@ -0,0 +1,428 @@ +/* $Id: rbtree.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include + +static void left_rotate( pj_rbtree *tree, pj_rbtree_node *node ) +{ + pj_rbtree_node *rnode, *parent; + + PJ_CHECK_STACK(); + + rnode = node->right; + if (rnode == tree->null) + return; + + node->right = rnode->left; + if (rnode->left != tree->null) + rnode->left->parent = node; + parent = node->parent; + rnode->parent = parent; + if (parent != tree->null) { + if (parent->left == node) + parent->left = rnode; + else + parent->right = rnode; + } else { + tree->root = rnode; + } + rnode->left = node; + node->parent = rnode; +} + +static void right_rotate( pj_rbtree *tree, pj_rbtree_node *node ) +{ + pj_rbtree_node *lnode, *parent; + + PJ_CHECK_STACK(); + + lnode = node->left; + if (lnode == tree->null) + return; + + node->left = lnode->right; + if (lnode->right != tree->null) + lnode->right->parent = node; + parent = node->parent; + lnode->parent = parent; + + if (parent != tree->null) { + if (parent->left == node) + parent->left = lnode; + else + parent->right = lnode; + } else { + tree->root = lnode; + } + lnode->right = node; + node->parent = lnode; +} + +static void insert_fixup( pj_rbtree *tree, pj_rbtree_node *node ) +{ + pj_rbtree_node *temp, *parent; + + PJ_CHECK_STACK(); + + while (node != tree->root && node->parent->color == PJ_RBCOLOR_RED) { + parent = node->parent; + if (parent == parent->parent->left) { + temp = parent->parent->right; + if (temp->color == PJ_RBCOLOR_RED) { + temp->color = PJ_RBCOLOR_BLACK; + node = parent; + node->color = PJ_RBCOLOR_BLACK; + node = node->parent; + node->color = PJ_RBCOLOR_RED; + } else { + if (node == parent->right) { + node = parent; + left_rotate(tree, node); + } + temp = node->parent; + temp->color = PJ_RBCOLOR_BLACK; + temp = temp->parent; + temp->color = PJ_RBCOLOR_RED; + right_rotate( tree, temp); + } + } else { + temp = parent->parent->left; + if (temp->color == PJ_RBCOLOR_RED) { + temp->color = PJ_RBCOLOR_BLACK; + node = parent; + node->color = PJ_RBCOLOR_BLACK; + node = node->parent; + node->color = PJ_RBCOLOR_RED; + } else { + if (node == parent->left) { + node = parent; + right_rotate(tree, node); + } + temp = node->parent; + temp->color = PJ_RBCOLOR_BLACK; + temp = temp->parent; + temp->color = PJ_RBCOLOR_RED; + left_rotate(tree, temp); + } + } + } + + tree->root->color = PJ_RBCOLOR_BLACK; +} + + +static void delete_fixup( pj_rbtree *tree, pj_rbtree_node *node ) +{ + pj_rbtree_node *temp; + + PJ_CHECK_STACK(); + + while (node != tree->root && node->color == PJ_RBCOLOR_BLACK) { + if (node->parent->left == node) { + temp = node->parent->right; + if (temp->color == PJ_RBCOLOR_RED) { + temp->color = PJ_RBCOLOR_BLACK; + node->parent->color = PJ_RBCOLOR_RED; + left_rotate(tree, node->parent); + temp = node->parent->right; + } + if (temp->left->color == PJ_RBCOLOR_BLACK && + temp->right->color == PJ_RBCOLOR_BLACK) + { + temp->color = PJ_RBCOLOR_RED; + node = node->parent; + } else { + if (temp->right->color == PJ_RBCOLOR_BLACK) { + temp->left->color = PJ_RBCOLOR_BLACK; + temp->color = PJ_RBCOLOR_RED; + right_rotate( tree, temp); + temp = node->parent->right; + } + temp->color = node->parent->color; + temp->right->color = PJ_RBCOLOR_BLACK; + node->parent->color = PJ_RBCOLOR_BLACK; + left_rotate(tree, node->parent); + node = tree->root; + } + } else { + temp = node->parent->left; + if (temp->color == PJ_RBCOLOR_RED) { + temp->color = PJ_RBCOLOR_BLACK; + node->parent->color = PJ_RBCOLOR_RED; + right_rotate( tree, node->parent); + temp = node->parent->left; + } + if (temp->right->color == PJ_RBCOLOR_BLACK && + temp->left->color == PJ_RBCOLOR_BLACK) + { + temp->color = PJ_RBCOLOR_RED; + node = node->parent; + } else { + if (temp->left->color == PJ_RBCOLOR_BLACK) { + temp->right->color = PJ_RBCOLOR_BLACK; + temp->color = PJ_RBCOLOR_RED; + left_rotate( tree, temp); + temp = node->parent->left; + } + temp->color = node->parent->color; + node->parent->color = PJ_RBCOLOR_BLACK; + temp->left->color = PJ_RBCOLOR_BLACK; + right_rotate(tree, node->parent); + node = tree->root; + } + } + } + + node->color = PJ_RBCOLOR_BLACK; +} + + +PJ_DEF(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp ) +{ + PJ_CHECK_STACK(); + + tree->null = tree->root = &tree->null_node; + tree->null->key = NULL; + tree->null->user_data = NULL; + tree->size = 0; + tree->null->left = tree->null->right = tree->null->parent = tree->null; + tree->null->color = PJ_RBCOLOR_BLACK; + tree->comp = comp; +} + +PJ_DEF(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree ) +{ + register pj_rbtree_node *node = tree->root; + register pj_rbtree_node *null = tree->null; + + PJ_CHECK_STACK(); + + while (node->left != null) + node = node->left; + return node != null ? node : NULL; +} + +PJ_DEF(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree ) +{ + register pj_rbtree_node *node = tree->root; + register pj_rbtree_node *null = tree->null; + + PJ_CHECK_STACK(); + + while (node->right != null) + node = node->right; + return node != null ? node : NULL; +} + +PJ_DEF(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree, + register pj_rbtree_node *node ) +{ + register pj_rbtree_node *null = tree->null; + + PJ_CHECK_STACK(); + + if (node->right != null) { + for (node=node->right; node->left!=null; node = node->left) + /* void */; + } else { + register pj_rbtree_node *temp = node->parent; + while (temp!=null && temp->right==node) { + node = temp; + temp = temp->parent; + } + node = temp; + } + return node != null ? node : NULL; +} + +PJ_DEF(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree, + register pj_rbtree_node *node ) +{ + register pj_rbtree_node *null = tree->null; + + PJ_CHECK_STACK(); + + if (node->left != null) { + for (node=node->left; node->right!=null; node=node->right) + /* void */; + } else { + register pj_rbtree_node *temp = node->parent; + while (temp!=null && temp->left==node) { + node = temp; + temp = temp->parent; + } + node = temp; + } + return node != null ? node : NULL; +} + +PJ_DEF(int) pj_rbtree_insert( pj_rbtree *tree, + pj_rbtree_node *element ) +{ + int rv = 0; + pj_rbtree_node *node, *parent = tree->null, + *null = tree->null; + pj_rbtree_comp *comp = tree->comp; + + PJ_CHECK_STACK(); + + node = tree->root; + while (node != null) { + rv = (*comp)(element->key, node->key); + if (rv == 0) { + /* found match, i.e. entry with equal key already exist */ + return -1; + } + parent = node; + node = rv < 0 ? node->left : node->right; + } + + element->color = PJ_RBCOLOR_RED; + element->left = element->right = null; + + node = element; + if (parent != null) { + node->parent = parent; + if (rv < 0) + parent->left = node; + else + parent->right = node; + insert_fixup( tree, node); + } else { + tree->root = node; + node->parent = null; + node->color = PJ_RBCOLOR_BLACK; + } + + ++tree->size; + return 0; +} + + +PJ_DEF(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree, + const void *key ) +{ + int rv; + pj_rbtree_node *node = tree->root; + pj_rbtree_node *null = tree->null; + pj_rbtree_comp *comp = tree->comp; + + while (node != null) { + rv = (*comp)(key, node->key); + if (rv == 0) + return node; + node = rv < 0 ? node->left : node->right; + } + return node != null ? node : NULL; +} + +PJ_DEF(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree, + pj_rbtree_node *node ) +{ + pj_rbtree_node *succ; + pj_rbtree_node *null = tree->null; + pj_rbtree_node *child; + pj_rbtree_node *parent; + + PJ_CHECK_STACK(); + + if (node->left == null || node->right == null) { + succ = node; + } else { + for (succ=node->right; succ->left!=null; succ=succ->left) + /* void */; + } + + child = succ->left != null ? succ->left : succ->right; + parent = succ->parent; + child->parent = parent; + + if (parent != null) { + if (parent->left == succ) + parent->left = child; + else + parent->right = child; + } else + tree->root = child; + + if (succ != node) { + succ->parent = node->parent; + succ->left = node->left; + succ->right = node->right; + succ->color = node->color; + + parent = node->parent; + if (parent != null) { + if (parent->left==node) + parent->left=succ; + else + parent->right=succ; + } + if (node->left != null) + node->left->parent = succ;; + if (node->right != null) + node->right->parent = succ; + + if (tree->root == node) + tree->root = succ; + } + + if (succ->color == PJ_RBCOLOR_BLACK) { + if (child != null) + delete_fixup(tree, child); + tree->null->color = PJ_RBCOLOR_BLACK; + } + + --tree->size; + return node; +} + + +PJ_DEF(unsigned) pj_rbtree_max_height( pj_rbtree *tree, + pj_rbtree_node *node ) +{ + unsigned l, r; + + PJ_CHECK_STACK(); + + if (node==NULL) + node = tree->root; + + l = node->left != tree->null ? pj_rbtree_max_height(tree,node->left)+1 : 0; + r = node->right != tree->null ? pj_rbtree_max_height(tree,node->right)+1 : 0; + return l > r ? l : r; +} + +PJ_DEF(unsigned) pj_rbtree_min_height( pj_rbtree *tree, + pj_rbtree_node *node ) +{ + unsigned l, r; + + PJ_CHECK_STACK(); + + if (node==NULL) + node=tree->root; + + l = (node->left != tree->null) ? pj_rbtree_max_height(tree,node->left)+1 : 0; + r = (node->right != tree->null) ? pj_rbtree_max_height(tree,node->right)+1 : 0; + return l > r ? r : l; +} + + diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c new file mode 100644 index 0000000..ed9fde9 --- /dev/null +++ b/pjlib/src/pj/sock_bsd.c @@ -0,0 +1,848 @@ +/* $Id: sock_bsd.c 4170 2012-06-19 07:40:19Z bennylp $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/* + * Address families conversion. + * The values here are indexed based on pj_addr_family. + */ +const pj_uint16_t PJ_AF_UNSPEC = AF_UNSPEC; +const pj_uint16_t PJ_AF_UNIX = AF_UNIX; +const pj_uint16_t PJ_AF_INET = AF_INET; +const pj_uint16_t PJ_AF_INET6 = AF_INET6; +#ifdef AF_PACKET +const pj_uint16_t PJ_AF_PACKET = AF_PACKET; +#else +const pj_uint16_t PJ_AF_PACKET = 0xFFFF; +#endif +#ifdef AF_IRDA +const pj_uint16_t PJ_AF_IRDA = AF_IRDA; +#else +const pj_uint16_t PJ_AF_IRDA = 0xFFFF; +#endif + +/* + * Socket types conversion. + * The values here are indexed based on pj_sock_type + */ +const pj_uint16_t PJ_SOCK_STREAM= SOCK_STREAM; +const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM; +const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW; +const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM; + +/* + * Socket level values. + */ +const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET; +#ifdef SOL_IP +const pj_uint16_t PJ_SOL_IP = SOL_IP; +#elif defined(PJ_WIN32) && PJ_WIN32 +const pj_uint16_t PJ_SOL_IP = IPPROTO_IP; +#else +const pj_uint16_t PJ_SOL_IP = 0; +#endif /* SOL_IP */ + +#if defined(SOL_TCP) +const pj_uint16_t PJ_SOL_TCP = SOL_TCP; +#elif defined(IPPROTO_TCP) +const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP; +#elif defined(PJ_WIN32) && PJ_WIN32 +const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP; +#else +const pj_uint16_t PJ_SOL_TCP = 6; +#endif /* SOL_TCP */ + +#ifdef SOL_UDP +const pj_uint16_t PJ_SOL_UDP = SOL_UDP; +#elif defined(IPPROTO_UDP) +const pj_uint16_t PJ_SOL_UDP = IPPROTO_UDP; +#elif defined(PJ_WIN32) && PJ_WIN32 +const pj_uint16_t PJ_SOL_UDP = IPPROTO_UDP; +#else +const pj_uint16_t PJ_SOL_UDP = 17; +#endif /* SOL_UDP */ + +#ifdef SOL_IPV6 +const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6; +#elif defined(PJ_WIN32) && PJ_WIN32 +# if defined(IPPROTO_IPV6) || (_WIN32_WINNT >= 0x0501) + const pj_uint16_t PJ_SOL_IPV6 = IPPROTO_IPV6; +# else + const pj_uint16_t PJ_SOL_IPV6 = 41; +# endif +#else +const pj_uint16_t PJ_SOL_IPV6 = 41; +#endif /* SOL_IPV6 */ + +/* IP_TOS */ +#ifdef IP_TOS +const pj_uint16_t PJ_IP_TOS = IP_TOS; +#else +const pj_uint16_t PJ_IP_TOS = 1; +#endif + + +/* TOS settings (declared in netinet/ip.h) */ +#ifdef IPTOS_LOWDELAY +const pj_uint16_t PJ_IPTOS_LOWDELAY = IPTOS_LOWDELAY; +#else +const pj_uint16_t PJ_IPTOS_LOWDELAY = 0x10; +#endif +#ifdef IPTOS_THROUGHPUT +const pj_uint16_t PJ_IPTOS_THROUGHPUT = IPTOS_THROUGHPUT; +#else +const pj_uint16_t PJ_IPTOS_THROUGHPUT = 0x08; +#endif +#ifdef IPTOS_RELIABILITY +const pj_uint16_t PJ_IPTOS_RELIABILITY = IPTOS_RELIABILITY; +#else +const pj_uint16_t PJ_IPTOS_RELIABILITY = 0x04; +#endif +#ifdef IPTOS_MINCOST +const pj_uint16_t PJ_IPTOS_MINCOST = IPTOS_MINCOST; +#else +const pj_uint16_t PJ_IPTOS_MINCOST = 0x02; +#endif + + +/* optname values. */ +const pj_uint16_t PJ_SO_TYPE = SO_TYPE; +const pj_uint16_t PJ_SO_RCVBUF = SO_RCVBUF; +const pj_uint16_t PJ_SO_SNDBUF = SO_SNDBUF; +const pj_uint16_t PJ_TCP_NODELAY= TCP_NODELAY; +const pj_uint16_t PJ_SO_REUSEADDR= SO_REUSEADDR; +#ifdef SO_NOSIGPIPE +const pj_uint16_t PJ_SO_NOSIGPIPE = SO_NOSIGPIPE; +#else +const pj_uint16_t PJ_SO_NOSIGPIPE = 0xFFFF; +#endif +#if defined(SO_PRIORITY) +const pj_uint16_t PJ_SO_PRIORITY = SO_PRIORITY; +#else +/* This is from Linux, YMMV */ +const pj_uint16_t PJ_SO_PRIORITY = 12; +#endif + +/* Multicasting is not supported e.g. in PocketPC 2003 SDK */ +#ifdef IP_MULTICAST_IF +const pj_uint16_t PJ_IP_MULTICAST_IF = IP_MULTICAST_IF; +const pj_uint16_t PJ_IP_MULTICAST_TTL = IP_MULTICAST_TTL; +const pj_uint16_t PJ_IP_MULTICAST_LOOP = IP_MULTICAST_LOOP; +const pj_uint16_t PJ_IP_ADD_MEMBERSHIP = IP_ADD_MEMBERSHIP; +const pj_uint16_t PJ_IP_DROP_MEMBERSHIP = IP_DROP_MEMBERSHIP; +#else +const pj_uint16_t PJ_IP_MULTICAST_IF = 0xFFFF; +const pj_uint16_t PJ_IP_MULTICAST_TTL = 0xFFFF; +const pj_uint16_t PJ_IP_MULTICAST_LOOP = 0xFFFF; +const pj_uint16_t PJ_IP_ADD_MEMBERSHIP = 0xFFFF; +const pj_uint16_t PJ_IP_DROP_MEMBERSHIP = 0xFFFF; +#endif + +/* recv() and send() flags */ +const int PJ_MSG_OOB = MSG_OOB; +const int PJ_MSG_PEEK = MSG_PEEK; +const int PJ_MSG_DONTROUTE = MSG_DONTROUTE; + + +#if 0 +static void CHECK_ADDR_LEN(const pj_sockaddr *addr, int len) +{ + pj_sockaddr *a = (pj_sockaddr*)addr; + pj_assert((a->addr.sa_family==PJ_AF_INET && len==sizeof(pj_sockaddr_in)) || + (a->addr.sa_family==PJ_AF_INET6 && len==sizeof(pj_sockaddr_in6))); + +} +#else +#define CHECK_ADDR_LEN(addr,len) +#endif + +/* + * Convert 16-bit value from network byte order to host byte order. + */ +PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort) +{ + return ntohs(netshort); +} + +/* + * Convert 16-bit value from host byte order to network byte order. + */ +PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort) +{ + return htons(hostshort); +} + +/* + * Convert 32-bit value from network byte order to host byte order. + */ +PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong) +{ + return ntohl(netlong); +} + +/* + * Convert 32-bit value from host byte order to network byte order. + */ +PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong) +{ + return htonl(hostlong); +} + +/* + * Convert an Internet host address given in network byte order + * to string in standard numbers and dots notation. + */ +PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr) +{ +#if !defined(PJ_LINUX) && !defined(PJ_LINUX_KERNEL) + return inet_ntoa(*(struct in_addr*)&inaddr); +#else + struct in_addr addr; + addr.s_addr = inaddr.s_addr; + return inet_ntoa(addr); +#endif +} + +/* + * This function converts the Internet host address cp from the standard + * numbers-and-dots notation into binary data and stores it in the structure + * that inp points to. + */ +PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp) +{ + char tempaddr[PJ_INET_ADDRSTRLEN]; + + /* Initialize output with PJ_INADDR_NONE. + * Some apps relies on this instead of the return value + * (and anyway the return value is quite confusing!) + */ + inp->s_addr = PJ_INADDR_NONE; + + /* Caution: + * this function might be called with cp->slen >= 16 + * (i.e. when called with hostname to check if it's an IP addr). + */ + PJ_ASSERT_RETURN(cp && cp->slen && inp, 0); + if (cp->slen >= PJ_INET_ADDRSTRLEN) { + return 0; + } + + pj_memcpy(tempaddr, cp->ptr, cp->slen); + tempaddr[cp->slen] = '\0'; + +#if defined(PJ_SOCK_HAS_INET_ATON) && PJ_SOCK_HAS_INET_ATON != 0 + return inet_aton(tempaddr, (struct in_addr*)inp); +#else + inp->s_addr = inet_addr(tempaddr); + return inp->s_addr == PJ_INADDR_NONE ? 0 : 1; +#endif +} + +/* + * Convert text to IPv4/IPv6 address. + */ +PJ_DEF(pj_status_t) pj_inet_pton(int af, const pj_str_t *src, void *dst) +{ + char tempaddr[PJ_INET6_ADDRSTRLEN]; + + PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EAFNOTSUP); + PJ_ASSERT_RETURN(src && src->slen && dst, PJ_EINVAL); + + /* Initialize output with PJ_IN_ADDR_NONE for IPv4 (to be + * compatible with pj_inet_aton() + */ + if (af==PJ_AF_INET) { + ((pj_in_addr*)dst)->s_addr = PJ_INADDR_NONE; + } + + /* Caution: + * this function might be called with cp->slen >= 46 + * (i.e. when called with hostname to check if it's an IP addr). + */ + if (src->slen >= PJ_INET6_ADDRSTRLEN) { + return PJ_ENAMETOOLONG; + } + + pj_memcpy(tempaddr, src->ptr, src->slen); + tempaddr[src->slen] = '\0'; + +#if defined(PJ_SOCK_HAS_INET_PTON) && PJ_SOCK_HAS_INET_PTON != 0 + /* + * Implementation using inet_pton() + */ + if (inet_pton(af, tempaddr, dst) != 1) { + pj_status_t status = pj_get_netos_error(); + if (status == PJ_SUCCESS) + status = PJ_EUNKNOWN; + + return status; + } + + return PJ_SUCCESS; + +#elif defined(PJ_WIN32) || defined(PJ_WIN32_WINCE) + /* + * Implementation on Windows, using WSAStringToAddress(). + * Should also work on Unicode systems. + */ + { + PJ_DECL_UNICODE_TEMP_BUF(wtempaddr,PJ_INET6_ADDRSTRLEN) + pj_sockaddr sock_addr; + int addr_len = sizeof(sock_addr); + int rc; + + sock_addr.addr.sa_family = (pj_uint16_t)af; + rc = WSAStringToAddress( + PJ_STRING_TO_NATIVE(tempaddr,wtempaddr,sizeof(wtempaddr)), + af, NULL, (LPSOCKADDR)&sock_addr, &addr_len); + if (rc != 0) { + /* If you get rc 130022 Invalid argument (WSAEINVAL) with IPv6, + * check that you have IPv6 enabled (install it in the network + * adapter). + */ + pj_status_t status = pj_get_netos_error(); + if (status == PJ_SUCCESS) + status = PJ_EUNKNOWN; + + return status; + } + + if (sock_addr.addr.sa_family == PJ_AF_INET) { + pj_memcpy(dst, &sock_addr.ipv4.sin_addr, 4); + return PJ_SUCCESS; + } else if (sock_addr.addr.sa_family == PJ_AF_INET6) { + pj_memcpy(dst, &sock_addr.ipv6.sin6_addr, 16); + return PJ_SUCCESS; + } else { + pj_assert(!"Shouldn't happen"); + return PJ_EBUG; + } + } +#elif !defined(PJ_HAS_IPV6) || PJ_HAS_IPV6==0 + /* IPv6 support is disabled, just return error without raising assertion */ + return PJ_EIPV6NOTSUP; +#else + pj_assert(!"Not supported"); + return PJ_EIPV6NOTSUP; +#endif +} + +/* + * Convert IPv4/IPv6 address to text. + */ +PJ_DEF(pj_status_t) pj_inet_ntop(int af, const void *src, + char *dst, int size) + +{ + PJ_ASSERT_RETURN(src && dst && size, PJ_EINVAL); + + *dst = '\0'; + + PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EAFNOTSUP); + +#if defined(PJ_SOCK_HAS_INET_NTOP) && PJ_SOCK_HAS_INET_NTOP != 0 + /* + * Implementation using inet_ntop() + */ + if (inet_ntop(af, src, dst, size) == NULL) { + pj_status_t status = pj_get_netos_error(); + if (status == PJ_SUCCESS) + status = PJ_EUNKNOWN; + + return status; + } + + return PJ_SUCCESS; + +#elif defined(PJ_WIN32) || defined(PJ_WIN32_WINCE) + /* + * Implementation on Windows, using WSAAddressToString(). + * Should also work on Unicode systems. + */ + { + PJ_DECL_UNICODE_TEMP_BUF(wtempaddr,PJ_INET6_ADDRSTRLEN) + pj_sockaddr sock_addr; + DWORD addr_len, addr_str_len; + int rc; + + pj_bzero(&sock_addr, sizeof(sock_addr)); + sock_addr.addr.sa_family = (pj_uint16_t)af; + if (af == PJ_AF_INET) { + if (size < PJ_INET_ADDRSTRLEN) + return PJ_ETOOSMALL; + pj_memcpy(&sock_addr.ipv4.sin_addr, src, 4); + addr_len = sizeof(pj_sockaddr_in); + addr_str_len = PJ_INET_ADDRSTRLEN; + } else if (af == PJ_AF_INET6) { + if (size < PJ_INET6_ADDRSTRLEN) + return PJ_ETOOSMALL; + pj_memcpy(&sock_addr.ipv6.sin6_addr, src, 16); + addr_len = sizeof(pj_sockaddr_in6); + addr_str_len = PJ_INET6_ADDRSTRLEN; + } else { + pj_assert(!"Unsupported address family"); + return PJ_EAFNOTSUP; + } + +#if PJ_NATIVE_STRING_IS_UNICODE + rc = WSAAddressToString((LPSOCKADDR)&sock_addr, addr_len, + NULL, wtempaddr, &addr_str_len); + if (rc == 0) { + pj_unicode_to_ansi(wtempaddr, wcslen(wtempaddr), dst, size); + } +#else + rc = WSAAddressToString((LPSOCKADDR)&sock_addr, addr_len, + NULL, dst, &addr_str_len); +#endif + + if (rc != 0) { + pj_status_t status = pj_get_netos_error(); + if (status == PJ_SUCCESS) + status = PJ_EUNKNOWN; + + return status; + } + + return PJ_SUCCESS; + } + +#elif !defined(PJ_HAS_IPV6) || PJ_HAS_IPV6==0 + /* IPv6 support is disabled, just return error without raising assertion */ + return PJ_EIPV6NOTSUP; +#else + pj_assert(!"Not supported"); + return PJ_EIPV6NOTSUP; +#endif +} + +/* + * Get hostname. + */ +PJ_DEF(const pj_str_t*) pj_gethostname(void) +{ + static char buf[PJ_MAX_HOSTNAME]; + static pj_str_t hostname; + + PJ_CHECK_STACK(); + + if (hostname.ptr == NULL) { + hostname.ptr = buf; + if (gethostname(buf, sizeof(buf)) != 0) { + hostname.ptr[0] = '\0'; + hostname.slen = 0; + } else { + hostname.slen = strlen(buf); + } + } + return &hostname; +} + +#if defined(PJ_WIN32) +/* + * Create new socket/endpoint for communication and returns a descriptor. + */ +PJ_DEF(pj_status_t) pj_sock_socket(int af, + int type, + int proto, + pj_sock_t *sock) +{ + PJ_CHECK_STACK(); + + /* Sanity checks. */ + PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL); + PJ_ASSERT_RETURN((unsigned)PJ_INVALID_SOCKET==INVALID_SOCKET, + (*sock=PJ_INVALID_SOCKET, PJ_EINVAL)); + + *sock = WSASocket(af, type, proto, NULL, 0, WSA_FLAG_OVERLAPPED); + + if (*sock == PJ_INVALID_SOCKET) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + +#if PJ_SOCK_DISABLE_WSAECONNRESET && \ + (!defined(PJ_WIN32_WINCE) || PJ_WIN32_WINCE==0) + +#ifndef SIO_UDP_CONNRESET + #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) +#endif + + /* Disable WSAECONNRESET for UDP. + * See https://trac.pjsip.org/repos/ticket/1197 + */ + if (type==PJ_SOCK_DGRAM) { + DWORD dwBytesReturned = 0; + BOOL bNewBehavior = FALSE; + DWORD rc; + + rc = WSAIoctl(*sock, SIO_UDP_CONNRESET, + &bNewBehavior, sizeof(bNewBehavior), + NULL, 0, &dwBytesReturned, + NULL, NULL); + + if (rc==SOCKET_ERROR) { + // Ignored.. + } + } +#endif + + return PJ_SUCCESS; +} + +#else +/* + * Create new socket/endpoint for communication and returns a descriptor. + */ +PJ_DEF(pj_status_t) pj_sock_socket(int af, + int type, + int proto, + pj_sock_t *sock) +{ + + PJ_CHECK_STACK(); + + /* Sanity checks. */ + PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL); + PJ_ASSERT_RETURN(PJ_INVALID_SOCKET==-1, + (*sock=PJ_INVALID_SOCKET, PJ_EINVAL)); + + *sock = socket(af, type, proto); + if (*sock == PJ_INVALID_SOCKET) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else { + pj_int32_t val = 1; + if (type == pj_SOCK_STREAM()) { + pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), pj_SO_NOSIGPIPE(), + &val, sizeof(val)); + } +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 + if (type == pj_SOCK_DGRAM()) { + pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), SO_NOSIGPIPE, + &val, sizeof(val)); + } +#endif + return PJ_SUCCESS; + } +} +#endif + +/* + * Bind socket. + */ +PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock, + const pj_sockaddr_t *addr, + int len) +{ + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(addr && len >= (int)sizeof(struct sockaddr_in), PJ_EINVAL); + + CHECK_ADDR_LEN(addr, len); + + if (bind(sock, (struct sockaddr*)addr, len) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + + +/* + * Bind socket. + */ +PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock, + pj_uint32_t addr32, + pj_uint16_t port) +{ + pj_sockaddr_in addr; + + PJ_CHECK_STACK(); + + PJ_SOCKADDR_SET_LEN(&addr, sizeof(pj_sockaddr_in)); + addr.sin_family = PJ_AF_INET; + pj_bzero(addr.sin_zero, sizeof(addr.sin_zero)); + addr.sin_addr.s_addr = pj_htonl(addr32); + addr.sin_port = pj_htons(port); + + return pj_sock_bind(sock, &addr, sizeof(pj_sockaddr_in)); +} + + +/* + * Close socket. + */ +PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sock) +{ + int rc; + + PJ_CHECK_STACK(); +#if defined(PJ_WIN32) && PJ_WIN32!=0 || \ + defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 + rc = closesocket(sock); +#else + rc = close(sock); +#endif + + if (rc != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Get remote's name. + */ +PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sock, + pj_sockaddr_t *addr, + int *namelen) +{ + PJ_CHECK_STACK(); + if (getpeername(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else { + PJ_SOCKADDR_RESET_LEN(addr); + return PJ_SUCCESS; + } +} + +/* + * Get socket name. + */ +PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock, + pj_sockaddr_t *addr, + int *namelen) +{ + PJ_CHECK_STACK(); + if (getsockname(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else { + PJ_SOCKADDR_RESET_LEN(addr); + return PJ_SUCCESS; + } +} + +/* + * Send data + */ +PJ_DEF(pj_status_t) pj_sock_send(pj_sock_t sock, + const void *buf, + pj_ssize_t *len, + unsigned flags) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(len, PJ_EINVAL); + +#ifdef MSG_NOSIGNAL + /* Suppress SIGPIPE. See https://trac.pjsip.org/repos/ticket/1538 */ + flags |= MSG_NOSIGNAL; +#endif + + *len = send(sock, (const char*)buf, *len, flags); + + if (*len < 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + + +/* + * Send data. + */ +PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock, + const void *buf, + pj_ssize_t *len, + unsigned flags, + const pj_sockaddr_t *to, + int tolen) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(len, PJ_EINVAL); + + CHECK_ADDR_LEN(to, tolen); + + *len = sendto(sock, (const char*)buf, *len, flags, + (const struct sockaddr*)to, tolen); + + if (*len < 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Receive data. + */ +PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock, + void *buf, + pj_ssize_t *len, + unsigned flags) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(buf && len, PJ_EINVAL); + + *len = recv(sock, (char*)buf, *len, flags); + + if (*len < 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Receive data. + */ +PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock, + void *buf, + pj_ssize_t *len, + unsigned flags, + pj_sockaddr_t *from, + int *fromlen) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(buf && len, PJ_EINVAL); + PJ_ASSERT_RETURN(from && fromlen, (*len=-1, PJ_EINVAL)); + + *len = recvfrom(sock, (char*)buf, *len, flags, + (struct sockaddr*)from, (socklen_t*)fromlen); + + if (*len < 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else { + PJ_SOCKADDR_RESET_LEN(from); + return PJ_SUCCESS; + } +} + +/* + * Get socket option. + */ +PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sock, + pj_uint16_t level, + pj_uint16_t optname, + void *optval, + int *optlen) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(optval && optlen, PJ_EINVAL); + + if (getsockopt(sock, level, optname, (char*)optval, (socklen_t*)optlen)!=0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Set socket option. + */ +PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock, + pj_uint16_t level, + pj_uint16_t optname, + const void *optval, + int optlen) +{ + PJ_CHECK_STACK(); + if (setsockopt(sock, level, optname, (const char*)optval, optlen) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Connect socket. + */ +PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sock, + const pj_sockaddr_t *addr, + int namelen) +{ + PJ_CHECK_STACK(); + if (connect(sock, (struct sockaddr*)addr, namelen) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + + +/* + * Shutdown socket. + */ +#if PJ_HAS_TCP +PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sock, + int how) +{ + PJ_CHECK_STACK(); + if (shutdown(sock, how) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Start listening to incoming connections. + */ +PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sock, + int backlog) +{ + PJ_CHECK_STACK(); + if (listen(sock, backlog) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Accept incoming connections + */ +PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd, + pj_sock_t *newsock, + pj_sockaddr_t *addr, + int *addrlen) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(newsock != NULL, PJ_EINVAL); + +#if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0 + if (addr) { + PJ_SOCKADDR_SET_LEN(addr, *addrlen); + } +#endif + + *newsock = accept(serverfd, (struct sockaddr*)addr, (socklen_t*)addrlen); + if (*newsock==PJ_INVALID_SOCKET) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else { + +#if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0 + if (addr) { + PJ_SOCKADDR_RESET_LEN(addr); + } +#endif + + return PJ_SUCCESS; + } +} +#endif /* PJ_HAS_TCP */ + + diff --git a/pjlib/src/pj/sock_common.c b/pjlib/src/pj/sock_common.c new file mode 100644 index 0000000..dd2ef6e --- /dev/null +++ b/pjlib/src/pj/sock_common.c @@ -0,0 +1,1196 @@ +/* $Id: sock_common.c 3841 2011-10-24 09:28:13Z ming $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 + /* Enable some tracing */ + #include + #define THIS_FILE "sock_common.c" + #define TRACE_(arg) PJ_LOG(4,arg) +#else + #define TRACE_(arg) +#endif + + +/* + * Convert address string with numbers and dots to binary IP address. + */ +PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp) +{ + pj_in_addr addr; + + pj_inet_aton(cp, &addr); + return addr; +} + +/* + * Convert address string with numbers and dots to binary IP address. + */ +PJ_DEF(pj_in_addr) pj_inet_addr2(const char *cp) +{ + pj_str_t str = pj_str((char*)cp); + return pj_inet_addr(&str); +} + +/* + * Get text representation. + */ +PJ_DEF(char*) pj_inet_ntop2( int af, const void *src, + char *dst, int size) +{ + pj_status_t status; + + status = pj_inet_ntop(af, src, dst, size); + return (status==PJ_SUCCESS)? dst : NULL; +} + +/* + * Print socket address. + */ +PJ_DEF(char*) pj_sockaddr_print( const pj_sockaddr_t *addr, + char *buf, int size, + unsigned flags) +{ + enum { + WITH_PORT = 1, + WITH_BRACKETS = 2 + }; + + char txt[PJ_INET6_ADDRSTRLEN]; + char port[32]; + const pj_addr_hdr *h = (const pj_addr_hdr*)addr; + char *bquote, *equote; + pj_status_t status; + + status = pj_inet_ntop(h->sa_family, pj_sockaddr_get_addr(addr), + txt, sizeof(txt)); + if (status != PJ_SUCCESS) + return ""; + + if (h->sa_family != PJ_AF_INET6 || (flags & WITH_BRACKETS)==0) { + bquote = ""; equote = ""; + } else { + bquote = "["; equote = "]"; + } + + if (flags & WITH_PORT) { + pj_ansi_snprintf(port, sizeof(port), ":%d", + pj_sockaddr_get_port(addr)); + } else { + port[0] = '\0'; + } + + pj_ansi_snprintf(buf, size, "%s%s%s%s", + bquote, txt, equote, port); + + return buf; +} + +/* + * Set the IP address of an IP socket address from string address, + * with resolving the host if necessary. The string address may be in a + * standard numbers and dots notation or may be a hostname. If hostname + * is specified, then the function will resolve the host into the IP + * address. + */ +PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, + const pj_str_t *str_addr) +{ + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(!str_addr || str_addr->slen < PJ_MAX_HOSTNAME, + (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL)); + + PJ_SOCKADDR_RESET_LEN(addr); + addr->sin_family = AF_INET; + pj_bzero(addr->sin_zero, sizeof(addr->sin_zero)); + + if (str_addr && str_addr->slen) { + addr->sin_addr = pj_inet_addr(str_addr); + if (addr->sin_addr.s_addr == PJ_INADDR_NONE) { + pj_hostent he; + pj_status_t rc; + + rc = pj_gethostbyname(str_addr, &he); + if (rc == 0) { + addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr; + } else { + addr->sin_addr.s_addr = PJ_INADDR_NONE; + return rc; + } + } + + } else { + addr->sin_addr.s_addr = 0; + } + + return PJ_SUCCESS; +} + +/* Set address from a name */ +PJ_DEF(pj_status_t) pj_sockaddr_set_str_addr(int af, + pj_sockaddr *addr, + const pj_str_t *str_addr) +{ + pj_status_t status; + + if (af == PJ_AF_INET) { + return pj_sockaddr_in_set_str_addr(&addr->ipv4, str_addr); + } + + PJ_ASSERT_RETURN(af==PJ_AF_INET6, PJ_EAFNOTSUP); + + /* IPv6 specific */ + + addr->ipv6.sin6_family = PJ_AF_INET6; + PJ_SOCKADDR_RESET_LEN(addr); + + if (str_addr && str_addr->slen) { + status = pj_inet_pton(PJ_AF_INET6, str_addr, &addr->ipv6.sin6_addr); + if (status != PJ_SUCCESS) { + pj_addrinfo ai; + unsigned count = 1; + + status = pj_getaddrinfo(PJ_AF_INET6, str_addr, &count, &ai); + if (status==PJ_SUCCESS) { + pj_memcpy(&addr->ipv6.sin6_addr, &ai.ai_addr.ipv6.sin6_addr, + sizeof(pj_sockaddr_in6)); + } + } + } else { + status = PJ_SUCCESS; + } + + return status; +} + +/* + * Set the IP address and port of an IP socket address. + * The string address may be in a standard numbers and dots notation or + * may be a hostname. If hostname is specified, then the function will + * resolve the host into the IP address. + */ +PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr, + const pj_str_t *str_addr, + pj_uint16_t port) +{ + PJ_ASSERT_RETURN(addr, (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL)); + + PJ_SOCKADDR_RESET_LEN(addr); + addr->sin_family = PJ_AF_INET; + pj_bzero(addr->sin_zero, sizeof(addr->sin_zero)); + pj_sockaddr_in_set_port(addr, port); + return pj_sockaddr_in_set_str_addr(addr, str_addr); +} + +/* + * Initialize IP socket address based on the address and port info. + */ +PJ_DEF(pj_status_t) pj_sockaddr_init(int af, + pj_sockaddr *addr, + const pj_str_t *cp, + pj_uint16_t port) +{ + pj_status_t status; + + if (af == PJ_AF_INET) { + return pj_sockaddr_in_init(&addr->ipv4, cp, port); + } + + /* IPv6 specific */ + PJ_ASSERT_RETURN(af==PJ_AF_INET6, PJ_EAFNOTSUP); + + pj_bzero(addr, sizeof(pj_sockaddr_in6)); + addr->addr.sa_family = PJ_AF_INET6; + + status = pj_sockaddr_set_str_addr(af, addr, cp); + if (status != PJ_SUCCESS) + return status; + + addr->ipv6.sin6_port = pj_htons(port); + return PJ_SUCCESS; +} + +/* + * Compare two socket addresses. + */ +PJ_DEF(int) pj_sockaddr_cmp( const pj_sockaddr_t *addr1, + const pj_sockaddr_t *addr2) +{ + const pj_sockaddr *a1 = (const pj_sockaddr*) addr1; + const pj_sockaddr *a2 = (const pj_sockaddr*) addr2; + int port1, port2; + int result; + + /* Compare address family */ + if (a1->addr.sa_family < a2->addr.sa_family) + return -1; + else if (a1->addr.sa_family > a2->addr.sa_family) + return 1; + + /* Compare addresses */ + result = pj_memcmp(pj_sockaddr_get_addr(a1), + pj_sockaddr_get_addr(a2), + pj_sockaddr_get_addr_len(a1)); + if (result != 0) + return result; + + /* Compare port number */ + port1 = pj_sockaddr_get_port(a1); + port2 = pj_sockaddr_get_port(a2); + + if (port1 < port2) + return -1; + else if (port1 > port2) + return 1; + + /* TODO: + * Do we need to compare flow label and scope id in IPv6? + */ + + /* Looks equal */ + return 0; +} + +/* + * Get first IP address associated with the hostname. + */ +PJ_DEF(pj_in_addr) pj_gethostaddr(void) +{ + pj_sockaddr_in addr; + const pj_str_t *hostname = pj_gethostname(); + + pj_sockaddr_in_set_str_addr(&addr, hostname); + return addr.sin_addr; +} + +/* + * Get port number of a pj_sockaddr_in + */ +PJ_DEF(pj_uint16_t) pj_sockaddr_in_get_port(const pj_sockaddr_in *addr) +{ + return pj_ntohs(addr->sin_port); +} + +/* + * Get the address part + */ +PJ_DEF(void*) pj_sockaddr_get_addr(const pj_sockaddr_t *addr) +{ + const pj_sockaddr *a = (const pj_sockaddr*)addr; + + PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET || + a->addr.sa_family == PJ_AF_INET6, NULL); + + if (a->addr.sa_family == PJ_AF_INET6) + return (void*) &a->ipv6.sin6_addr; + else + return (void*) &a->ipv4.sin_addr; +} + +/* + * Check if sockaddr contains a non-zero address + */ +PJ_DEF(pj_bool_t) pj_sockaddr_has_addr(const pj_sockaddr_t *addr) +{ + const pj_sockaddr *a = (const pj_sockaddr*)addr; + + /* It's probably not wise to raise assertion here if + * the address doesn't contain a valid address family, and + * just return PJ_FALSE instead. + * + * The reason is because application may need to distinguish + * these three conditions with sockaddr: + * a) sockaddr is not initialized. This is by convention + * indicated by sa_family==0. + * b) sockaddr is initialized with zero address. This is + * indicated with the address field having zero address. + * c) sockaddr is initialized with valid address/port. + * + * If we enable this assertion, then application will loose + * the capability to specify condition a), since it will be + * forced to always initialize sockaddr (even with zero address). + * This may break some parts of upper layer libraries. + */ + //PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET || + // a->addr.sa_family == PJ_AF_INET6, PJ_FALSE); + + if (a->addr.sa_family!=PJ_AF_INET && a->addr.sa_family!=PJ_AF_INET6) { + return PJ_FALSE; + } else if (a->addr.sa_family == PJ_AF_INET6) { + pj_uint8_t zero[24]; + pj_bzero(zero, sizeof(zero)); + return pj_memcmp(a->ipv6.sin6_addr.s6_addr, zero, + sizeof(pj_in6_addr)) != 0; + } else + return a->ipv4.sin_addr.s_addr != PJ_INADDR_ANY; +} + +/* + * Get port number + */ +PJ_DEF(pj_uint16_t) pj_sockaddr_get_port(const pj_sockaddr_t *addr) +{ + const pj_sockaddr *a = (const pj_sockaddr*) addr; + + PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET || + a->addr.sa_family == PJ_AF_INET6, (pj_uint16_t)0xFFFF); + + return pj_ntohs((pj_uint16_t)(a->addr.sa_family == PJ_AF_INET6 ? + a->ipv6.sin6_port : a->ipv4.sin_port)); +} + +/* + * Get the length of the address part. + */ +PJ_DEF(unsigned) pj_sockaddr_get_addr_len(const pj_sockaddr_t *addr) +{ + const pj_sockaddr *a = (const pj_sockaddr*) addr; + PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET || + a->addr.sa_family == PJ_AF_INET6, 0); + return a->addr.sa_family == PJ_AF_INET6 ? + sizeof(pj_in6_addr) : sizeof(pj_in_addr); +} + +/* + * Get socket address length. + */ +PJ_DEF(unsigned) pj_sockaddr_get_len(const pj_sockaddr_t *addr) +{ + const pj_sockaddr *a = (const pj_sockaddr*) addr; + PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET || + a->addr.sa_family == PJ_AF_INET6, 0); + return a->addr.sa_family == PJ_AF_INET6 ? + sizeof(pj_sockaddr_in6) : sizeof(pj_sockaddr_in); +} + +/* + * Copy only the address part (sin_addr/sin6_addr) of a socket address. + */ +PJ_DEF(void) pj_sockaddr_copy_addr( pj_sockaddr *dst, + const pj_sockaddr *src) +{ + /* Destination sockaddr might not be initialized */ + const char *srcbuf = (char*)pj_sockaddr_get_addr(src); + char *dstbuf = ((char*)dst) + (srcbuf - (char*)src); + pj_memcpy(dstbuf, srcbuf, pj_sockaddr_get_addr_len(src)); +} + +/* + * Copy socket address. + */ +PJ_DEF(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src) +{ + pj_memcpy(dst, src, pj_sockaddr_get_len(src)); +} + +/* + * Set port number of pj_sockaddr_in + */ +PJ_DEF(void) pj_sockaddr_in_set_port(pj_sockaddr_in *addr, + pj_uint16_t hostport) +{ + addr->sin_port = pj_htons(hostport); +} + +/* + * Set port number of pj_sockaddr + */ +PJ_DEF(pj_status_t) pj_sockaddr_set_port(pj_sockaddr *addr, + pj_uint16_t hostport) +{ + int af = addr->addr.sa_family; + + PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL); + + if (af == PJ_AF_INET6) + addr->ipv6.sin6_port = pj_htons(hostport); + else + addr->ipv4.sin_port = pj_htons(hostport); + + return PJ_SUCCESS; +} + +/* + * Get IPv4 address + */ +PJ_DEF(pj_in_addr) pj_sockaddr_in_get_addr(const pj_sockaddr_in *addr) +{ + pj_in_addr in_addr; + in_addr.s_addr = pj_ntohl(addr->sin_addr.s_addr); + return in_addr; +} + +/* + * Set IPv4 address + */ +PJ_DEF(void) pj_sockaddr_in_set_addr(pj_sockaddr_in *addr, + pj_uint32_t hostaddr) +{ + addr->sin_addr.s_addr = pj_htonl(hostaddr); +} + +/* + * Parse address + */ +PJ_DEF(pj_status_t) pj_sockaddr_parse2(int af, unsigned options, + const pj_str_t *str, + pj_str_t *p_hostpart, + pj_uint16_t *p_port, + int *raf) +{ + const char *end = str->ptr + str->slen; + const char *last_colon_pos = NULL; + unsigned colon_cnt = 0; + const char *p; + + PJ_ASSERT_RETURN((af==PJ_AF_INET || af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) && + options==0 && + str!=NULL, PJ_EINVAL); + + /* Special handling for empty input */ + if (str->slen==0 || str->ptr==NULL) { + if (p_hostpart) + p_hostpart->slen = 0; + if (p_port) + *p_port = 0; + if (raf) + *raf = PJ_AF_INET; + return PJ_SUCCESS; + } + + /* Count the colon and get the last colon */ + for (p=str->ptr; p!=end; ++p) { + if (*p == ':') { + ++colon_cnt; + last_colon_pos = p; + } + } + + /* Deduce address family if it's not given */ + if (af == PJ_AF_UNSPEC) { + if (colon_cnt > 1) + af = PJ_AF_INET6; + else + af = PJ_AF_INET; + } else if (af == PJ_AF_INET && colon_cnt > 1) + return PJ_EINVAL; + + if (raf) + *raf = af; + + if (af == PJ_AF_INET) { + /* Parse as IPv4. Supported formats: + * - "10.0.0.1:80" + * - "10.0.0.1" + * - "10.0.0.1:" + * - ":80" + * - ":" + */ + pj_str_t hostpart; + unsigned long port; + + hostpart.ptr = (char*)str->ptr; + + if (last_colon_pos) { + pj_str_t port_part; + int i; + + hostpart.slen = last_colon_pos - str->ptr; + + port_part.ptr = (char*)last_colon_pos + 1; + port_part.slen = end - port_part.ptr; + + /* Make sure port number is valid */ + for (i=0; i 65535) + return PJ_EINVAL; + } else { + hostpart.slen = str->slen; + port = 0; + } + + if (p_hostpart) + *p_hostpart = hostpart; + if (p_port) + *p_port = (pj_uint16_t)port; + + return PJ_SUCCESS; + + } else if (af == PJ_AF_INET6) { + + /* Parse as IPv6. Supported formats: + * - "fe::01:80" ==> note: port number is zero in this case, not 80! + * - "[fe::01]:80" + * - "fe::01" + * - "fe::01:" + * - "[fe::01]" + * - "[fe::01]:" + * - "[::]:80" + * - ":::80" + * - "[::]" + * - "[::]:" + * - ":::" + * - "::" + */ + pj_str_t hostpart, port_part; + + if (*str->ptr == '[') { + char *end_bracket; + int i; + unsigned long port; + + if (last_colon_pos == NULL) + return PJ_EINVAL; + + end_bracket = pj_strchr(str, ']'); + if (end_bracket == NULL) + return PJ_EINVAL; + + hostpart.ptr = (char*)str->ptr + 1; + hostpart.slen = end_bracket - hostpart.ptr; + + if (last_colon_pos < end_bracket) { + port_part.ptr = NULL; + port_part.slen = 0; + } else { + port_part.ptr = (char*)last_colon_pos + 1; + port_part.slen = end - port_part.ptr; + } + + /* Make sure port number is valid */ + for (i=0; i 65535) + return PJ_EINVAL; + + if (p_hostpart) + *p_hostpart = hostpart; + if (p_port) + *p_port = (pj_uint16_t)port; + + return PJ_SUCCESS; + + } else { + /* Treat everything as part of the IPv6 IP address */ + if (p_hostpart) + *p_hostpart = *str; + if (p_port) + *p_port = 0; + + return PJ_SUCCESS; + } + + } else { + return PJ_EAFNOTSUP; + } + +} + +/* + * Parse address + */ +PJ_DEF(pj_status_t) pj_sockaddr_parse( int af, unsigned options, + const pj_str_t *str, + pj_sockaddr *addr) +{ + pj_str_t hostpart; + pj_uint16_t port; + pj_status_t status; + + PJ_ASSERT_RETURN(addr, PJ_EINVAL); + PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC || + af==PJ_AF_INET || + af==PJ_AF_INET6, PJ_EINVAL); + PJ_ASSERT_RETURN(options == 0, PJ_EINVAL); + + status = pj_sockaddr_parse2(af, options, str, &hostpart, &port, &af); + if (status != PJ_SUCCESS) + return status; + +#if !defined(PJ_HAS_IPV6) || !PJ_HAS_IPV6 + if (af==PJ_AF_INET6) + return PJ_EIPV6NOTSUP; +#endif + + status = pj_sockaddr_init(af, addr, &hostpart, port); +#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 + if (status != PJ_SUCCESS && af == PJ_AF_INET6) { + /* Parsing does not yield valid address. Try to treat the last + * portion after the colon as port number. + */ + const char *last_colon_pos=NULL, *p; + const char *end = str->ptr + str->slen; + unsigned long long_port; + pj_str_t port_part; + int i; + + /* Parse as IPv6:port */ + for (p=str->ptr; p!=end; ++p) { + if (*p == ':') + last_colon_pos = p; + } + + if (last_colon_pos == NULL) + return status; + + hostpart.ptr = (char*)str->ptr; + hostpart.slen = last_colon_pos - str->ptr; + + port_part.ptr = (char*)last_colon_pos + 1; + port_part.slen = end - port_part.ptr; + + /* Make sure port number is valid */ + for (i=0; i 65535) + return status; + + port = (pj_uint16_t)long_port; + + status = pj_sockaddr_init(PJ_AF_INET6, addr, &hostpart, port); + } +#endif + + return status; +} + +/* Resolve the IP address of local machine */ +PJ_DEF(pj_status_t) pj_gethostip(int af, pj_sockaddr *addr) +{ + unsigned i, count, cand_cnt; + enum { + CAND_CNT = 8, + + /* Weighting to be applied to found addresses */ + WEIGHT_HOSTNAME = 1, /* hostname IP is not always valid! */ + WEIGHT_DEF_ROUTE = 2, + WEIGHT_INTERFACE = 1, + WEIGHT_LOOPBACK = -5, + WEIGHT_LINK_LOCAL = -4, + WEIGHT_DISABLED = -50, + + MIN_WEIGHT = WEIGHT_DISABLED+1 /* minimum weight to use */ + }; + /* candidates: */ + pj_sockaddr cand_addr[CAND_CNT]; + int cand_weight[CAND_CNT]; + int selected_cand; + char strip[PJ_INET6_ADDRSTRLEN+10]; + /* Special IPv4 addresses. */ + struct spec_ipv4_t + { + pj_uint32_t addr; + pj_uint32_t mask; + int weight; + } spec_ipv4[] = + { + /* 127.0.0.0/8, loopback addr will be used if there is no other + * addresses. + */ + { 0x7f000000, 0xFF000000, WEIGHT_LOOPBACK }, + + /* 0.0.0.0/8, special IP that doesn't seem to be practically useful */ + { 0x00000000, 0xFF000000, WEIGHT_DISABLED }, + + /* 169.254.0.0/16, a zeroconf/link-local address, which has higher + * priority than loopback and will be used if there is no other + * valid addresses. + */ + { 0xa9fe0000, 0xFFFF0000, WEIGHT_LINK_LOCAL } + }; + /* Special IPv6 addresses */ + struct spec_ipv6_t + { + pj_uint8_t addr[16]; + pj_uint8_t mask[16]; + int weight; + } spec_ipv6[] = + { + /* Loopback address, ::1/128 */ + { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, + {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, + WEIGHT_LOOPBACK + }, + + /* Link local, fe80::/10 */ + { {0xfe,0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0xff,0xc0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + WEIGHT_LINK_LOCAL + }, + + /* Disabled, ::/128 */ + { {0x0,0x0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, + WEIGHT_DISABLED + } + }; + pj_addrinfo ai; + pj_status_t status; + + /* May not be used if TRACE_ is disabled */ + PJ_UNUSED_ARG(strip); + +#ifdef _MSC_VER + /* Get rid of "uninitialized he variable" with MS compilers */ + pj_bzero(&ai, sizeof(ai)); +#endif + + cand_cnt = 0; + pj_bzero(cand_addr, sizeof(cand_addr)); + pj_bzero(cand_weight, sizeof(cand_weight)); + for (i=0; iaddr.sa_family = (pj_uint16_t)af; + PJ_SOCKADDR_RESET_LEN(addr); + +#if !defined(PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION) || \ + PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION == 0 + /* Get hostname's IP address */ + count = 1; + status = pj_getaddrinfo(af, pj_gethostname(), &count, &ai); + if (status == PJ_SUCCESS) { + pj_assert(ai.ai_addr.addr.sa_family == (pj_uint16_t)af); + pj_sockaddr_copy_addr(&cand_addr[cand_cnt], &ai.ai_addr); + pj_sockaddr_set_port(&cand_addr[cand_cnt], 0); + cand_weight[cand_cnt] += WEIGHT_HOSTNAME; + ++cand_cnt; + + TRACE_((THIS_FILE, "hostname IP is %s", + pj_sockaddr_print(&ai.ai_addr, strip, sizeof(strip), 0))); + } +#else + PJ_UNUSED_ARG(ai); + PJ_UNUSED_ARG(count); +#endif + + /* Get default interface (interface for default route) */ + if (cand_cnt < PJ_ARRAY_SIZE(cand_addr)) { + status = pj_getdefaultipinterface(af, addr); + if (status == PJ_SUCCESS) { + TRACE_((THIS_FILE, "default IP is %s", + pj_sockaddr_print(addr, strip, sizeof(strip), 0))); + + pj_sockaddr_set_port(addr, 0); + for (i=0; i= cand_cnt) { + pj_sockaddr_copy_addr(&cand_addr[i], addr); + ++cand_cnt; + } + } + } + + + /* Enumerate IP interfaces */ + if (cand_cnt < PJ_ARRAY_SIZE(cand_addr)) { + unsigned start_if = cand_cnt; + unsigned count = PJ_ARRAY_SIZE(cand_addr) - start_if; + + status = pj_enum_ip_interface(af, &count, &cand_addr[start_if]); + if (status == PJ_SUCCESS && count) { + /* Clear the port number */ + for (i=0; i cand_weight[selected_cand]) + selected_cand = i; + } + + /* If else fails, returns loopback interface as the last resort */ + if (selected_cand == -1) { + if (af==PJ_AF_INET) { + addr->ipv4.sin_addr.s_addr = pj_htonl (0x7f000001); + } else { + pj_in6_addr *s6_addr; + + s6_addr = (pj_in6_addr*) pj_sockaddr_get_addr(addr); + pj_bzero(s6_addr, sizeof(pj_in6_addr)); + s6_addr->s6_addr[15] = 1; + } + TRACE_((THIS_FILE, "Loopback IP %s returned", + pj_sockaddr_print(addr, strip, sizeof(strip), 0))); + } else { + pj_sockaddr_copy_addr(addr, &cand_addr[selected_cand]); + TRACE_((THIS_FILE, "Candidate %s selected", + pj_sockaddr_print(addr, strip, sizeof(strip), 0))); + } + + return PJ_SUCCESS; +} + +/* Get the default IP interface */ +PJ_DEF(pj_status_t) pj_getdefaultipinterface(int af, pj_sockaddr *addr) +{ + pj_sock_t fd; + pj_str_t cp; + pj_sockaddr a; + int len; + pj_uint8_t zero[64]; + pj_status_t status; + + addr->addr.sa_family = (pj_uint16_t)af; + + status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &fd); + if (status != PJ_SUCCESS) { + return status; + } + + if (af == PJ_AF_INET) { + cp = pj_str("1.1.1.1"); + } else { + cp = pj_str("1::1"); + } + status = pj_sockaddr_init(af, &a, &cp, 53); + if (status != PJ_SUCCESS) { + pj_sock_close(fd); + return status; + } + + status = pj_sock_connect(fd, &a, pj_sockaddr_get_len(&a)); + if (status != PJ_SUCCESS) { + pj_sock_close(fd); + return status; + } + + len = sizeof(a); + status = pj_sock_getsockname(fd, &a, &len); + if (status != PJ_SUCCESS) { + pj_sock_close(fd); + return status; + } + + pj_sock_close(fd); + + /* Check that the address returned is not zero */ + pj_bzero(zero, sizeof(zero)); + if (pj_memcmp(pj_sockaddr_get_addr(&a), zero, + pj_sockaddr_get_addr_len(&a))==0) + { + return PJ_ENOTFOUND; + } + + pj_sockaddr_copy_addr(addr, &a); + + /* Success */ + return PJ_SUCCESS; +} + + +/* Only need to implement these in DLL build */ +#if defined(PJ_DLL) + +PJ_DEF(pj_uint16_t) pj_AF_UNSPEC(void) +{ + return PJ_AF_UNSPEC; +} + +PJ_DEF(pj_uint16_t) pj_AF_UNIX(void) +{ + return PJ_AF_UNIX; +} + +PJ_DEF(pj_uint16_t) pj_AF_INET(void) +{ + return PJ_AF_INET; +} + +PJ_DEF(pj_uint16_t) pj_AF_INET6(void) +{ + return PJ_AF_INET6; +} + +PJ_DEF(pj_uint16_t) pj_AF_PACKET(void) +{ + return PJ_AF_PACKET; +} + +PJ_DEF(pj_uint16_t) pj_AF_IRDA(void) +{ + return PJ_AF_IRDA; +} + +PJ_DEF(int) pj_SOCK_STREAM(void) +{ + return PJ_SOCK_STREAM; +} + +PJ_DEF(int) pj_SOCK_DGRAM(void) +{ + return PJ_SOCK_DGRAM; +} + +PJ_DEF(int) pj_SOCK_RAW(void) +{ + return PJ_SOCK_RAW; +} + +PJ_DEF(int) pj_SOCK_RDM(void) +{ + return PJ_SOCK_RDM; +} + +PJ_DEF(pj_uint16_t) pj_SOL_SOCKET(void) +{ + return PJ_SOL_SOCKET; +} + +PJ_DEF(pj_uint16_t) pj_SOL_IP(void) +{ + return PJ_SOL_IP; +} + +PJ_DEF(pj_uint16_t) pj_SOL_TCP(void) +{ + return PJ_SOL_TCP; +} + +PJ_DEF(pj_uint16_t) pj_SOL_UDP(void) +{ + return PJ_SOL_UDP; +} + +PJ_DEF(pj_uint16_t) pj_SOL_IPV6(void) +{ + return PJ_SOL_IPV6; +} + +PJ_DEF(int) pj_IP_TOS(void) +{ + return PJ_IP_TOS; +} + +PJ_DEF(int) pj_IPTOS_LOWDELAY(void) +{ + return PJ_IPTOS_LOWDELAY; +} + +PJ_DEF(int) pj_IPTOS_THROUGHPUT(void) +{ + return PJ_IPTOS_THROUGHPUT; +} + +PJ_DEF(int) pj_IPTOS_RELIABILITY(void) +{ + return PJ_IPTOS_RELIABILITY; +} + +PJ_DEF(int) pj_IPTOS_MINCOST(void) +{ + return PJ_IPTOS_MINCOST; +} + +PJ_DEF(pj_uint16_t) pj_SO_TYPE(void) +{ + return PJ_SO_TYPE; +} + +PJ_DEF(pj_uint16_t) pj_SO_RCVBUF(void) +{ + return PJ_SO_RCVBUF; +} + +PJ_DEF(pj_uint16_t) pj_SO_SNDBUF(void) +{ + return PJ_SO_SNDBUF; +} + +PJ_DEF(pj_uint16_t) pj_TCP_NODELAY(void) +{ + return PJ_TCP_NODELAY; +} + +PJ_DEF(pj_uint16_t) pj_SO_REUSEADDR(void) +{ + return PJ_SO_REUSEADDR; +} + +PJ_DEF(pj_uint16_t) pj_SO_NOSIGPIPE(void) +{ + return PJ_SO_NOSIGPIPE; +} + +PJ_DEF(pj_uint16_t) pj_SO_PRIORITY(void) +{ + return PJ_SO_PRIORITY; +} + +PJ_DEF(pj_uint16_t) pj_IP_MULTICAST_IF(void) +{ + return PJ_IP_MULTICAST_IF; +} + +PJ_DEF(pj_uint16_t) pj_IP_MULTICAST_TTL(void) +{ + return PJ_IP_MULTICAST_TTL; +} + +PJ_DEF(pj_uint16_t) pj_IP_MULTICAST_LOOP(void) +{ + return PJ_IP_MULTICAST_LOOP; +} + +PJ_DEF(pj_uint16_t) pj_IP_ADD_MEMBERSHIP(void) +{ + return PJ_IP_ADD_MEMBERSHIP; +} + +PJ_DEF(pj_uint16_t) pj_IP_DROP_MEMBERSHIP(void) +{ + return PJ_IP_DROP_MEMBERSHIP; +} + +PJ_DEF(int) pj_MSG_OOB(void) +{ + return PJ_MSG_OOB; +} + +PJ_DEF(int) pj_MSG_PEEK(void) +{ + return PJ_MSG_PEEK; +} + +PJ_DEF(int) pj_MSG_DONTROUTE(void) +{ + return PJ_MSG_DONTROUTE; +} + +#endif /* PJ_DLL */ + diff --git a/pjlib/src/pj/sock_linux_kernel.c b/pjlib/src/pj/sock_linux_kernel.c new file mode 100644 index 0000000..a1a7c7c --- /dev/null +++ b/pjlib/src/pj/sock_linux_kernel.c @@ -0,0 +1,755 @@ +/* $Id: sock_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include /* pj_memcpy() */ +#include /* PJ_CHECK_STACK() */ +#include /* pj_gethostbyname() */ +#include +#include +#include +#include + +/* Linux kernel specific. */ +#include +#include +//#include +#include +#include /* sys_xxx() */ +#include /* FIONBIO */ +#include /* for pj_gethostname() */ + +/* + * Address families conversion. + * The values here are indexed based on pj_addr_family-0xFF00. + */ +const pj_uint16_t PJ_AF_UNIX = AF_UNIX; +const pj_uint16_t PJ_AF_INET = AF_INET; +const pj_uint16_t PJ_AF_INET6 = AF_INET6; +#ifdef AF_PACKET +const pj_uint16_t PJ_AF_PACKET = AF_PACKET; +#else +# error "AF_PACKET undeclared!" +#endif +#ifdef AF_IRDA +const pj_uint16_t PJ_AF_IRDA = AF_IRDA; +#else +# error "AF_IRDA undeclared!" +#endif + +/* + * Socket types conversion. + * The values here are indexed based on pj_sock_type-0xFF00 + */ +const pj_uint16_t PJ_SOCK_STREAM= SOCK_STREAM; +const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM; +const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW; +const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM; + +/* + * Socket level values. + */ +const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET; +#ifdef SOL_IP +const pj_uint16_t PJ_SOL_IP = SOL_IP; +#else +# error "SOL_IP undeclared!" +#endif /* SOL_IP */ +#if defined(SOL_TCP) +const pj_uint16_t PJ_SOL_TCP = SOL_TCP; +#else +# error "SOL_TCP undeclared!" +#endif /* SOL_TCP */ +#ifdef SOL_UDP +const pj_uint16_t PJ_SOL_UDP = SOL_UDP; +#else +# error "SOL_UDP undeclared!" +#endif +#ifdef SOL_IPV6 +const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6; +#else +# error "SOL_IPV6 undeclared!" +#endif + +/* optname values. */ +const pj_uint16_t PJ_SO_TYPE = SO_TYPE; +const pj_uint16_t PJ_SO_RCVBUF = SO_RCVBUF; +const pj_uint16_t PJ_SO_SNDBUF = SO_SNDBUF; + +/* + * Convert 16-bit value from network byte order to host byte order. + */ +PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort) +{ + return ntohs(netshort); +} + +/* + * Convert 16-bit value from host byte order to network byte order. + */ +PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort) +{ + return htons(hostshort); +} + +/* + * Convert 32-bit value from network byte order to host byte order. + */ +PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong) +{ + return ntohl(netlong); +} + +/* + * Convert 32-bit value from host byte order to network byte order. + */ +PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong) +{ + return htonl(hostlong); +} + +/* + * Convert an Internet host address given in network byte order + * to string in standard numbers and dots notation. + */ +PJ_DEF(char*) pj_inet_ntoa(pj_in_addr in) +{ +#define UC(b) (((int)b)&0xff) + static char b[18]; + char *p; + + p = (char *)∈ + pj_snprintf(b, sizeof(b), "%d.%d.%d.%d", + UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); + + return b; +} + +/* + * This function converts the Internet host address ccp from the standard + * numbers-and-dots notation into binary data and stores it in the structure + * that inp points to. + */ +PJ_DEF(int) pj_inet_aton(const pj_str_t *ccp, struct pj_in_addr *addr) +{ + pj_uint32_t val; + int base, n; + char c; + unsigned parts[4]; + unsigned *pp = parts; + char cp_copy[18]; + char *cp = cp_copy; + + addr->s_addr = PJ_INADDR_NONE; + + if (ccp->slen > 15) return 0; + + pj_memcpy(cp, ccp->ptr, ccp->slen); + cp[ccp->slen] = '\0'; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, isdigit=decimal. + */ + if (!pj_isdigit((int)c)) + return (0); + val = 0; base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') + base = 16, c = *++cp; + else + base = 8; + } + + for (;;) { + if (pj_isascii((int)c) && pj_isdigit((int)c)) { + val = (val * base) + (c - '0'); + c = *++cp; + } else if (base==16 && pj_isascii((int)c) && pj_isxdigit((int)c)) { + val = (val << 4) | + (c + 10 - (pj_islower((int)c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) + return (0); + *pp++ = val; + c = *++cp; + } else + break; + } + + /* + * Check for trailing characters. + */ + if (c != '\0' && (!pj_isascii((int)c) || !pj_isspace((int)c))) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + switch (n) { + case 0: + return (0); /* initial nondigit */ + case 1: /* a -- 32 bits */ + break; + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffff) + return (0); + val |= parts[0] << 24; + break; + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + + if (addr) + addr->s_addr = pj_htonl(val); + return (1); +} + +/* + * Convert address string with numbers and dots to binary IP address. + */ +PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp) +{ + pj_in_addr addr; + pj_inet_aton(cp, &addr); + return addr; +} + +/* + * Set the IP address of an IP socket address from string address, + * with resolving the host if necessary. The string address may be in a + * standard numbers and dots notation or may be a hostname. If hostname + * is specified, then the function will resolve the host into the IP + * address. + */ +PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, + const pj_str_t *str_addr) +{ + PJ_CHECK_STACK(); + + pj_assert(str_addr && str_addr->slen < PJ_MAX_HOSTNAME); + + addr->sin_family = AF_INET; + + if (str_addr && str_addr->slen) { + addr->sin_addr = pj_inet_addr(str_addr); + if (addr->sin_addr.s_addr == PJ_INADDR_NONE) { + pj_hostent he; + if (pj_gethostbyname(str_addr, &he) == 0) { + addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr; + } else { + addr->sin_addr.s_addr = PJ_INADDR_NONE; + return -1; + } + } + + } else { + addr->sin_addr.s_addr = 0; + } + + return PJ_SUCCESS; +} + +/* + * Set the IP address and port of an IP socket address. + * The string address may be in a standard numbers and dots notation or + * may be a hostname. If hostname is specified, then the function will + * resolve the host into the IP address. + */ +PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr, + const pj_str_t *str_addr, + pj_uint16_t port) +{ + pj_assert(addr && str_addr); + + addr->sin_family = PJ_AF_INET; + pj_sockaddr_in_set_port(addr, port); + return pj_sockaddr_in_set_str_addr(addr, str_addr); +} + + +/* + * Get hostname. + */ +PJ_DEF(const pj_str_t*) pj_gethostname(void) +{ + static char buf[PJ_MAX_HOSTNAME]; + static pj_str_t hostname; + + PJ_CHECK_STACK(); + + if (hostname.ptr == NULL) { + hostname.ptr = buf; + down_read(&uts_sem); + hostname.slen = strlen(system_utsname.nodename); + if (hostname.slen > PJ_MAX_HOSTNAME) { + hostname.ptr[0] = '\0'; + hostname.slen = 0; + } else { + pj_memcpy(hostname.ptr, system_utsname.nodename, hostname.slen); + } + up_read(&uts_sem); + } + return &hostname; +} + +/* + * Get first IP address associated with the hostname. + */ +PJ_DEF(pj_in_addr) pj_gethostaddr(void) +{ + pj_sockaddr_in addr; + const pj_str_t *hostname = pj_gethostname(); + + pj_sockaddr_in_set_str_addr(&addr, hostname); + return addr.sin_addr; +} + + +/* + * Create new socket/endpoint for communication and returns a descriptor. + */ +PJ_DEF(pj_status_t) pj_sock_socket(int af, int type, int proto, + pj_sock_t *sock_fd) +{ + long result; + + PJ_CHECK_STACK(); + + /* Sanity checks. */ + PJ_ASSERT_RETURN(PJ_INVALID_SOCKET == -1 && sock_fd != NULL, PJ_EINVAL); + + /* Initialize returned socket */ + *sock_fd = PJ_INVALID_SOCKET; + + /* Create socket. */ + result = sys_socket(af, type, proto); + if (result < 0) { + return PJ_RETURN_OS_ERROR((-result)); + } + + *sock_fd = result; + + return PJ_SUCCESS; +} + +/* + * Bind socket. + */ +PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sockfd, + const pj_sockaddr_t *addr, + int len) +{ + long err; + mm_segment_t oldfs; + + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(addr!=NULL && len >= sizeof(struct pj_sockaddr), + PJ_EINVAL); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_bind(sockfd, (struct sockaddr*)addr, len); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + + +/* + * Bind socket. + */ +PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd, + pj_uint32_t addr32, + pj_uint16_t port) +{ + pj_sockaddr_in addr; + + PJ_CHECK_STACK(); + + addr.sin_family = PJ_AF_INET; + addr.sin_addr.s_addr = pj_htonl(addr32); + addr.sin_port = pj_htons(port); + + return pj_sock_bind(sockfd, &addr, sizeof(pj_sockaddr_in)); +} + +/* + * Close socket. + */ +PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sockfd) +{ + long err; + + err = sys_close(sockfd); + + if (err != 0) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Get remote's name. + */ +PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sockfd, + pj_sockaddr_t *addr, + int *namelen) +{ + mm_segment_t oldfs; + long err; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_getpeername( sockfd, addr, namelen); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Get socket name. + */ +PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd, + pj_sockaddr_t *addr, + int *namelen) +{ + mm_segment_t oldfs; + int err; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_getsockname( sockfd, addr, namelen ); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Send data + */ +PJ_DEF(pj_status_t) pj_sock_send( pj_sock_t sockfd, + const void *buf, + pj_ssize_t *len, + unsigned flags) +{ + return pj_sock_sendto(sockfd, buf, len, flags, NULL, 0); +} + + +/* + * Send data. + */ +PJ_DEF(pj_status_t) pj_sock_sendto( pj_sock_t sockfd, + const void *buff, + pj_ssize_t *len, + unsigned flags, + const pj_sockaddr_t *addr, + int addr_len) +{ + long err; + mm_segment_t oldfs; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = *len = sys_sendto( sockfd, (void*)buff, *len, flags, + (void*)addr, addr_len ); + + set_fs(oldfs); + + if (err >= 0) { + return PJ_SUCCESS; + } + else { + return PJ_RETURN_OS_ERROR(-err); + } +} + +/* + * Receive data. + */ +PJ_DEF(pj_status_t) pj_sock_recv( pj_sock_t sockfd, + void *buf, + pj_ssize_t *len, + unsigned flags) +{ + return pj_sock_recvfrom(sockfd, buf, len, flags, NULL, NULL); +} + +/* + * Receive data. + */ +PJ_DEF(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd, + void *buff, + pj_ssize_t *size, + unsigned flags, + pj_sockaddr_t *from, + int *fromlen) +{ + mm_segment_t oldfs; + long err; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = *size = sys_recvfrom( sockfd, buff, *size, flags, from, fromlen); + + set_fs(oldfs); + + if (err >= 0) { + return PJ_SUCCESS; + } + else { + return PJ_RETURN_OS_ERROR(-err); + } +} + +/* + * Get socket option. + */ +PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd, + pj_uint16_t level, + pj_uint16_t optname, + void *optval, + int *optlen) +{ + mm_segment_t oldfs; + long err; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_getsockopt( sockfd, level, optname, optval, optlen); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Set socket option. + */ +PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd, + pj_uint16_t level, + pj_uint16_t optname, + const void *optval, + int optlen) +{ + long err; + mm_segment_t oldfs; + + PJ_CHECK_STACK(); + + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_setsockopt( sockfd, level, optname, (void*)optval, optlen); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Shutdown socket. + */ +#if PJ_HAS_TCP +PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd, + int how) +{ + long err; + + PJ_CHECK_STACK(); + + err = sys_shutdown(sockfd, how); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Start listening to incoming connections. + */ +PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sockfd, + int backlog) +{ + long err; + + PJ_CHECK_STACK(); + + err = sys_listen( sockfd, backlog ); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Connect socket. + */ +PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sockfd, + const pj_sockaddr_t *addr, + int namelen) +{ + long err; + mm_segment_t oldfs; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_connect( sockfd, (void*)addr, namelen ); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Accept incoming connections + */ +PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t sockfd, + pj_sock_t *newsockfd, + pj_sockaddr_t *addr, + int *addrlen) +{ + long err; + + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(newsockfd != NULL, PJ_EINVAL); + + err = sys_accept( sockfd, addr, addrlen); + + if (err < 0) { + *newsockfd = PJ_INVALID_SOCKET; + return PJ_RETURN_OS_ERROR(-err); + } + else { + *newsockfd = err; + return PJ_SUCCESS; + } +} +#endif /* PJ_HAS_TCP */ + + + +/* + * Permission to steal inet_ntoa() and inet_aton() as long as this notice below + * is included: + */ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + diff --git a/pjlib/src/pj/sock_qos_bsd.c b/pjlib/src/pj/sock_qos_bsd.c new file mode 100644 index 0000000..ca28653 --- /dev/null +++ b/pjlib/src/pj/sock_qos_bsd.c @@ -0,0 +1,132 @@ +/* $Id: sock_qos_bsd.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-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 +#include +#include +#include + +/* This is the implementation of QoS with BSD socket's setsockopt(), + * using IP_TOS and SO_PRIORITY + */ +#if !defined(PJ_QOS_IMPLEMENTATION) || PJ_QOS_IMPLEMENTATION==PJ_QOS_BSD + +PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, + pj_qos_params *param) +{ + pj_status_t last_err = PJ_ENOTSUP; + pj_status_t status; + + /* No op? */ + if (!param->flags) + return PJ_SUCCESS; + + /* Clear WMM field since we don't support it */ + param->flags &= ~(PJ_QOS_PARAM_HAS_WMM); + + /* Set TOS/DSCP */ + if (param->flags & PJ_QOS_PARAM_HAS_DSCP) { + /* Value is dscp_val << 2 */ + int val = (param->dscp_val << 2); + status = pj_sock_setsockopt(sock, pj_SOL_IP(), pj_IP_TOS(), + &val, sizeof(val)); + if (status != PJ_SUCCESS) { + param->flags &= ~(PJ_QOS_PARAM_HAS_DSCP); + last_err = status; + } + } + + /* Set SO_PRIORITY */ + if (param->flags & PJ_QOS_PARAM_HAS_SO_PRIO) { + int val = param->so_prio; + status = pj_sock_setsockopt(sock, pj_SOL_SOCKET(), pj_SO_PRIORITY(), + &val, sizeof(val)); + if (status != PJ_SUCCESS) { + param->flags &= ~(PJ_QOS_PARAM_HAS_SO_PRIO); + last_err = status; + } + } + + return param->flags ? PJ_SUCCESS : last_err; +} + +PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, + pj_qos_type type) +{ + pj_qos_params param; + pj_status_t status; + + status = pj_qos_get_params(type, ¶m); + if (status != PJ_SUCCESS) + return status; + + return pj_sock_set_qos_params(sock, ¶m); +} + + +PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, + pj_qos_params *p_param) +{ + pj_status_t last_err = PJ_ENOTSUP; + int val, optlen; + pj_status_t status; + + pj_bzero(p_param, sizeof(*p_param)); + + /* Get DSCP/TOS value */ + optlen = sizeof(val); + status = pj_sock_getsockopt(sock, pj_SOL_IP(), pj_IP_TOS(), + &val, &optlen); + if (status == PJ_SUCCESS) { + p_param->flags |= PJ_QOS_PARAM_HAS_DSCP; + p_param->dscp_val = (pj_uint8_t)(val >> 2); + } else { + last_err = status; + } + + /* Get SO_PRIORITY */ + optlen = sizeof(val); + status = pj_sock_getsockopt(sock, pj_SOL_SOCKET(), pj_SO_PRIORITY(), + &val, &optlen); + if (status == PJ_SUCCESS) { + p_param->flags |= PJ_QOS_PARAM_HAS_SO_PRIO; + p_param->so_prio = (pj_uint8_t)val; + } else { + last_err = status; + } + + /* WMM is not supported */ + + return p_param->flags ? PJ_SUCCESS : last_err; +} + +PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, + pj_qos_type *p_type) +{ + pj_qos_params param; + pj_status_t status; + + status = pj_sock_get_qos_params(sock, ¶m); + if (status != PJ_SUCCESS) + return status; + + return pj_qos_get_type(¶m, p_type); +} + +#endif /* PJ_QOS_IMPLEMENTATION */ + diff --git a/pjlib/src/pj/sock_qos_common.c b/pjlib/src/pj/sock_qos_common.c new file mode 100644 index 0000000..643f0ea --- /dev/null +++ b/pjlib/src/pj/sock_qos_common.c @@ -0,0 +1,151 @@ +/* $Id: sock_qos_common.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-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 +#include +#include +#include +#include + +#define THIS_FILE "sock_qos_common.c" +#define ALL_FLAGS (PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_SO_PRIO | \ + PJ_QOS_PARAM_HAS_WMM) + +/* "Standard" mapping between traffic type and QoS params */ +static const pj_qos_params qos_map[] = +{ + /* flags dscp prio wmm_prio */ + {ALL_FLAGS, 0x00, 0, PJ_QOS_WMM_PRIO_BULK_EFFORT}, /* BE */ + {ALL_FLAGS, 0x08, 2, PJ_QOS_WMM_PRIO_BULK}, /* BK */ + {ALL_FLAGS, 0x28, 5, PJ_QOS_WMM_PRIO_VIDEO}, /* VI */ + {ALL_FLAGS, 0x30, 6, PJ_QOS_WMM_PRIO_VOICE}, /* VO */ + {ALL_FLAGS, 0x38, 7, PJ_QOS_WMM_PRIO_VOICE} /* CO */ +}; + + +/* Retrieve the mapping for the specified type */ +PJ_DEF(pj_status_t) pj_qos_get_params(pj_qos_type type, + pj_qos_params *p_param) +{ + PJ_ASSERT_RETURN(type<=PJ_QOS_TYPE_CONTROL && p_param, PJ_EINVAL); + pj_memcpy(p_param, &qos_map[type], sizeof(*p_param)); + return PJ_SUCCESS; +} + +/* Get the matching traffic type */ +PJ_DEF(pj_status_t) pj_qos_get_type( const pj_qos_params *param, + pj_qos_type *p_type) +{ + unsigned dscp_type = PJ_QOS_TYPE_BEST_EFFORT, + prio_type = PJ_QOS_TYPE_BEST_EFFORT, + wmm_type = PJ_QOS_TYPE_BEST_EFFORT; + unsigned i, count=0; + + PJ_ASSERT_RETURN(param && p_type, PJ_EINVAL); + + if (param->flags & PJ_QOS_PARAM_HAS_DSCP) { + for (i=0; i<=PJ_QOS_TYPE_CONTROL; ++i) { + if (param->dscp_val >= qos_map[i].dscp_val) + dscp_type = (pj_qos_type)i; + } + ++count; + } + + if (param->flags & PJ_QOS_PARAM_HAS_SO_PRIO) { + for (i=0; i<=PJ_QOS_TYPE_CONTROL; ++i) { + if (param->so_prio >= qos_map[i].so_prio) + prio_type = (pj_qos_type)i; + } + ++count; + } + + if (param->flags & PJ_QOS_PARAM_HAS_WMM) { + for (i=0; i<=PJ_QOS_TYPE_CONTROL; ++i) { + if (param->wmm_prio >= qos_map[i].wmm_prio) + wmm_type = (pj_qos_type)i; + } + ++count; + } + + if (count) + *p_type = (pj_qos_type)((dscp_type + prio_type + wmm_type) / count); + else + *p_type = PJ_QOS_TYPE_BEST_EFFORT; + + return PJ_SUCCESS; +} + +/* Apply QoS */ +PJ_DEF(pj_status_t) pj_sock_apply_qos( pj_sock_t sock, + pj_qos_type qos_type, + pj_qos_params *qos_params, + unsigned log_level, + const char *log_sender, + const char *sock_name) +{ + pj_status_t qos_type_rc = PJ_SUCCESS, + qos_params_rc = PJ_SUCCESS; + + if (!log_sender) + log_sender = THIS_FILE; + if (!sock_name) + sock_name = "socket"; + + if (qos_type != PJ_QOS_TYPE_BEST_EFFORT) { + qos_type_rc = pj_sock_set_qos_type(sock, qos_type); + + if (qos_type_rc != PJ_SUCCESS) { + pj_perror(log_level, log_sender, qos_type_rc, + "Error setting QoS type %d to %s", + qos_type, sock_name); + } + } + + if (qos_params && qos_params->flags) { + qos_params_rc = pj_sock_set_qos_params(sock, qos_params); + if (qos_params_rc != PJ_SUCCESS) { + pj_perror(log_level, log_sender, qos_params_rc, + "Error setting QoS params (flags=%d) to %s", + qos_params->flags, sock_name); + if (qos_type_rc != PJ_SUCCESS) + return qos_params_rc; + } + } else if (qos_type_rc != PJ_SUCCESS) + return qos_type_rc; + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_sock_apply_qos2( pj_sock_t sock, + pj_qos_type qos_type, + const pj_qos_params *qos_params, + unsigned log_level, + const char *log_sender, + const char *sock_name) +{ + pj_qos_params qos_params_buf, *qos_params_copy = NULL; + + if (qos_params) { + pj_memcpy(&qos_params_buf, qos_params, sizeof(*qos_params)); + qos_params_copy = &qos_params_buf; + } + + return pj_sock_apply_qos(sock, qos_type, qos_params_copy, + log_level, log_sender, sock_name); +} diff --git a/pjlib/src/pj/sock_qos_dummy.c b/pjlib/src/pj/sock_qos_dummy.c new file mode 100644 index 0000000..fbbb56f --- /dev/null +++ b/pjlib/src/pj/sock_qos_dummy.c @@ -0,0 +1,76 @@ +/* $Id: sock_qos_dummy.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2009-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 +#include +#include + +/* Dummy implementation of QoS API. + * (this is controlled by pjlib's config.h) + */ +#if defined(PJ_QOS_IMPLEMENTATION) && PJ_QOS_IMPLEMENTATION==PJ_QOS_DUMMY + +#define THIS_FILE "sock_qos_dummy.c" + + +PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, + pj_qos_params *param) +{ + PJ_UNUSED_ARG(sock); + PJ_UNUSED_ARG(param); + + PJ_LOG(4,(THIS_FILE, "pj_sock_set_qos_params() is not implemented " + "for this platform")); + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, + pj_qos_type type) +{ + PJ_UNUSED_ARG(sock); + PJ_UNUSED_ARG(type); + + PJ_LOG(4,(THIS_FILE, "pj_sock_set_qos_type() is not implemented " + "for this platform")); + return PJ_ENOTSUP; +} + + +PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, + pj_qos_params *p_param) +{ + PJ_UNUSED_ARG(sock); + PJ_UNUSED_ARG(p_param); + + PJ_LOG(4,(THIS_FILE, "pj_sock_get_qos_params() is not implemented " + "for this platform")); + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, + pj_qos_type *p_type) +{ + PJ_UNUSED_ARG(sock); + PJ_UNUSED_ARG(p_type); + + PJ_LOG(4,(THIS_FILE, "pj_sock_get_qos_type() is not implemented " + "for this platform")); + return PJ_ENOTSUP; +} + +#endif /* PJ_QOS_DUMMY */ diff --git a/pjlib/src/pj/sock_qos_symbian.cpp b/pjlib/src/pj/sock_qos_symbian.cpp new file mode 100644 index 0000000..35779b8 --- /dev/null +++ b/pjlib/src/pj/sock_qos_symbian.cpp @@ -0,0 +1,95 @@ +/* $Id: sock_qos_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2009-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 +#include "os_symbian.h" + +PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, + pj_qos_params *param) +{ + PJ_ASSERT_RETURN(sock!=0 && sock!=PJ_INVALID_SOCKET, PJ_EINVAL); + + CPjSocket *pjsock = (CPjSocket*)sock; + RSocket & rsock = pjsock->Socket(); + pj_status_t last_err = PJ_ENOTSUP; + + /* SO_PRIORITY and WMM are not supported */ + param->flags &= ~(PJ_QOS_PARAM_HAS_SO_PRIO | PJ_QOS_PARAM_HAS_WMM); + + if (param->flags & PJ_QOS_PARAM_HAS_DSCP) { + TInt err; + + err = rsock.SetOpt(KSoIpTOS, KProtocolInetIp, + (param->dscp_val << 2)); + if (err != KErrNone) { + last_err = PJ_RETURN_OS_ERROR(err); + param->flags &= ~(PJ_QOS_PARAM_HAS_DSCP); + } + } + + return param->flags ? PJ_SUCCESS : last_err; +} + +PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, + pj_qos_type type) +{ + pj_qos_params param; + pj_status_t status; + + status = pj_qos_get_params(type, ¶m); + if (status != PJ_SUCCESS) + return status; + + return pj_sock_set_qos_params(sock, ¶m); +} + + +PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, + pj_qos_params *p_param) +{ + PJ_ASSERT_RETURN(sock!=0 && sock!=PJ_INVALID_SOCKET, PJ_EINVAL); + + CPjSocket *pjsock = (CPjSocket*)sock; + RSocket & rsock = pjsock->Socket(); + TInt err, dscp; + + pj_bzero(p_param, sizeof(*p_param)); + + err = rsock.GetOpt(KSoIpTOS, KProtocolInetIp, dscp); + if (err == KErrNone) { + p_param->flags |= PJ_QOS_PARAM_HAS_DSCP; + p_param->dscp_val = (dscp >> 2); + return PJ_SUCCESS; + } else { + return PJ_RETURN_OS_ERROR(err); + } +} + +PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, + pj_qos_type *p_type) +{ + pj_qos_params param; + pj_status_t status; + + status = pj_sock_get_qos_params(sock, ¶m); + if (status != PJ_SUCCESS) + return status; + + return pj_qos_get_type(¶m, p_type); +} + diff --git a/pjlib/src/pj/sock_qos_wm.c b/pjlib/src/pj/sock_qos_wm.c new file mode 100644 index 0000000..e607d96 --- /dev/null +++ b/pjlib/src/pj/sock_qos_wm.c @@ -0,0 +1,103 @@ +/* $Id: sock_qos_wm.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2009-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 +#include +#include +#include + +#include + +/* QoS implementation for Windows Mobile 6, must be enabled explicitly + * (this is controlled by pjlib's config.h) + */ +#if defined(PJ_QOS_IMPLEMENTATION) && PJ_QOS_IMPLEMENTATION==PJ_QOS_WM + +#define THIS_FILE "sock_qos_wm.c" + +/* Mapping between our traffic type and WM's DSCP traffic types */ +static const int dscp_map[] = +{ + DSCPBestEffort, + DSCPBackground, + DSCPVideo, + DSCPAudio, + DSCPControl +}; + +PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, + pj_qos_params *param) +{ + PJ_UNUSED_ARG(sock); + PJ_UNUSED_ARG(param); + + PJ_LOG(4,(THIS_FILE, "pj_sock_set_qos_params() is not implemented " + "for this platform")); + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, + pj_qos_type type) +{ + int value; + + PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(dscp_map), PJ_EINVAL); + + value = dscp_map[type]; + return pj_sock_setsockopt(sock, IPPROTO_IP, IP_DSCP_TRAFFIC_TYPE, + &value, sizeof(value)); +} + + +PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, + pj_qos_params *p_param) +{ + PJ_UNUSED_ARG(sock); + PJ_UNUSED_ARG(p_param); + + PJ_LOG(4,(THIS_FILE, "pj_sock_get_qos_params() is not implemented " + "for this platform")); + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, + pj_qos_type *p_type) +{ + pj_status_t status; + int value, optlen; + unsigned i; + + optlen = sizeof(value); + value = 0; + status = pj_sock_getsockopt(sock, IPPROTO_IP, IP_DSCP_TRAFFIC_TYPE, + &value, &optlen); + if (status != PJ_SUCCESS) + return status; + + *p_type = PJ_QOS_TYPE_BEST_EFFORT; + for (i=0; i + * + * 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 +#include +#include +#include +#include + +#if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H!=0 +# include +#endif + +#if defined(PJ_HAS_SYS_TIME_H) && PJ_HAS_SYS_TIME_H!=0 +# include +#endif + +#ifdef _MSC_VER +# pragma warning(disable: 4018) // Signed/unsigned mismatch in FD_* +# pragma warning(disable: 4389) // Signed/unsigned mismatch in FD_* +#endif + +#define PART_FDSET(ps) ((fd_set*)&ps->data[1]) +#define PART_FDSET_OR_NULL(ps) (ps ? PART_FDSET(ps) : NULL) +#define PART_COUNT(ps) (ps->data[0]) + +PJ_DEF(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp) +{ + PJ_CHECK_STACK(); + pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set)); + + FD_ZERO(PART_FDSET(fdsetp)); + PART_COUNT(fdsetp) = 0; +} + + +PJ_DEF(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp) +{ + PJ_CHECK_STACK(); + pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set)); + + if (!PJ_FD_ISSET(fd, fdsetp)) + ++PART_COUNT(fdsetp); + FD_SET(fd, PART_FDSET(fdsetp)); +} + + +PJ_DEF(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp) +{ + PJ_CHECK_STACK(); + pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set)); + + if (PJ_FD_ISSET(fd, fdsetp)) + --PART_COUNT(fdsetp); + FD_CLR(fd, PART_FDSET(fdsetp)); +} + + +PJ_DEF(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set), + 0); + + return FD_ISSET(fd, PART_FDSET(fdsetp)); +} + +PJ_DEF(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp) +{ + return PART_COUNT(fdsetp); +} + +PJ_DEF(int) pj_sock_select( int n, + pj_fd_set_t *readfds, + pj_fd_set_t *writefds, + pj_fd_set_t *exceptfds, + const pj_time_val *timeout) +{ + struct timeval os_timeout, *p_os_timeout; + + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set), + PJ_EBUG); + + if (timeout) { + os_timeout.tv_sec = timeout->sec; + os_timeout.tv_usec = timeout->msec * 1000; + p_os_timeout = &os_timeout; + } else { + p_os_timeout = NULL; + } + + return select(n, PART_FDSET_OR_NULL(readfds), PART_FDSET_OR_NULL(writefds), + PART_FDSET_OR_NULL(exceptfds), p_os_timeout); +} + diff --git a/pjlib/src/pj/sock_select_symbian.cpp b/pjlib/src/pj/sock_select_symbian.cpp new file mode 100644 index 0000000..aeaa2bc --- /dev/null +++ b/pjlib/src/pj/sock_select_symbian.cpp @@ -0,0 +1,163 @@ +/* $Id: sock_select_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include "os_symbian.h" + + +struct symbian_fd_set +{ + unsigned count; + CPjSocket *sock[PJ_IOQUEUE_MAX_HANDLES]; +}; + + +PJ_DEF(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp) +{ + symbian_fd_set *fds = (symbian_fd_set *)fdsetp; + fds->count = 0; +} + + +PJ_DEF(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp) +{ + symbian_fd_set *fds = (symbian_fd_set *)fdsetp; + + PJ_ASSERT_ON_FAIL(fds->count < PJ_IOQUEUE_MAX_HANDLES, return); + fds->sock[fds->count++] = (CPjSocket*)fd; +} + + +PJ_DEF(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp) +{ + symbian_fd_set *fds = (symbian_fd_set *)fdsetp; + unsigned i; + + for (i=0; icount; ++i) { + if (fds->sock[i] == (CPjSocket*)fd) { + pj_array_erase(fds->sock, sizeof(fds->sock[0]), fds->count, i); + --fds->count; + return; + } + } +} + + +PJ_DEF(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp) +{ + symbian_fd_set *fds = (symbian_fd_set *)fdsetp; + unsigned i; + + for (i=0; icount; ++i) { + if (fds->sock[i] == (CPjSocket*)fd) { + return PJ_TRUE; + } + } + + return PJ_FALSE; +} + +PJ_DEF(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp) +{ + symbian_fd_set *fds = (symbian_fd_set *)fdsetp; + return fds->count; +} + + +PJ_DEF(int) pj_sock_select( int n, + pj_fd_set_t *readfds, + pj_fd_set_t *writefds, + pj_fd_set_t *exceptfds, + const pj_time_val *timeout) +{ + CPjTimeoutTimer *pjTimer; + unsigned i; + + PJ_UNUSED_ARG(n); + PJ_UNUSED_ARG(writefds); + PJ_UNUSED_ARG(exceptfds); + + if (timeout) { + pjTimer = PjSymbianOS::Instance()->SelectTimeoutTimer(); + pjTimer->StartTimer(timeout->sec*1000 + timeout->msec); + + } else { + pjTimer = NULL; + } + + /* Scan for readable sockets */ + + if (readfds) { + symbian_fd_set *fds = (symbian_fd_set *)readfds; + + do { + /* Scan sockets for readily available data */ + for (i=0; icount; ++i) { + CPjSocket *pjsock = fds->sock[i]; + + if (pjsock->Reader()) { + if (pjsock->Reader()->HasData() && !pjsock->Reader()->IsActive()) { + + /* Found socket with data ready */ + PJ_FD_ZERO(readfds); + PJ_FD_SET((pj_sock_t)pjsock, readfds); + + /* Cancel timer, if any */ + if (pjTimer) { + pjTimer->Cancel(); + } + + /* Clear writable and exception fd_set */ + if (writefds) + PJ_FD_ZERO(writefds); + if (exceptfds) + PJ_FD_ZERO(exceptfds); + + return 1; + + } else if (!pjsock->Reader()->IsActive()) + pjsock->Reader()->StartRecvFrom(); + + } else { + pjsock->CreateReader(); + pjsock->Reader()->StartRecvFrom(); + } + } + + PjSymbianOS::Instance()->WaitForActiveObjects(); + + } while (pjTimer==NULL || !pjTimer->HasTimedOut()); + } + + + /* Timeout */ + + if (readfds) + PJ_FD_ZERO(readfds); + if (writefds) + PJ_FD_ZERO(writefds); + if (exceptfds) + PJ_FD_ZERO(exceptfds); + + return 0; +} + diff --git a/pjlib/src/pj/sock_symbian.cpp b/pjlib/src/pj/sock_symbian.cpp new file mode 100644 index 0000000..35db1ea --- /dev/null +++ b/pjlib/src/pj/sock_symbian.cpp @@ -0,0 +1,1021 @@ +/* $Id: sock_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "os_symbian.h" + + +/* + * Address families. + */ +const pj_uint16_t PJ_AF_UNSPEC = KAFUnspec; +const pj_uint16_t PJ_AF_UNIX = 0xFFFF; +const pj_uint16_t PJ_AF_INET = KAfInet; +const pj_uint16_t PJ_AF_INET6 = KAfInet6; +const pj_uint16_t PJ_AF_PACKET = 0xFFFF; +const pj_uint16_t PJ_AF_IRDA = 0xFFFF; + +/* + * Socket types conversion. + * The values here are indexed based on pj_sock_type + */ +const pj_uint16_t PJ_SOCK_STREAM= KSockStream; +const pj_uint16_t PJ_SOCK_DGRAM = KSockDatagram; +const pj_uint16_t PJ_SOCK_RAW = 0xFFFF; +const pj_uint16_t PJ_SOCK_RDM = 0xFFFF; + +/* we don't support setsockopt(), these are just dummy values */ +const pj_uint16_t PJ_SOL_SOCKET = 0xFFFF; +const pj_uint16_t PJ_SOL_IP = 0xFFFF; +const pj_uint16_t PJ_SOL_TCP = 0xFFFF; +const pj_uint16_t PJ_SOL_UDP = 0xFFFF; +const pj_uint16_t PJ_SOL_IPV6 = 0xFFFF; +const pj_uint16_t PJ_SO_NOSIGPIPE = 0xFFFF; + +/* TOS */ +const pj_uint16_t PJ_IP_TOS = 0; +const pj_uint16_t PJ_IPTOS_LOWDELAY = 0; +const pj_uint16_t PJ_IPTOS_THROUGHPUT = 0; +const pj_uint16_t PJ_IPTOS_RELIABILITY = 0; +const pj_uint16_t PJ_IPTOS_MINCOST = 0; + +/* Misc */ +const pj_uint16_t PJ_TCP_NODELAY = 0xFFFF; +const pj_uint16_t PJ_SO_REUSEADDR = 0xFFFF; +const pj_uint16_t PJ_SO_PRIORITY = 0xFFFF; + +/* ioctl() is also not supported. */ +const pj_uint16_t PJ_SO_TYPE = 0xFFFF; +const pj_uint16_t PJ_SO_RCVBUF = 0xFFFF; +const pj_uint16_t PJ_SO_SNDBUF = 0xFFFF; + +/* IP multicast is also not supported. */ +const pj_uint16_t PJ_IP_MULTICAST_IF = 0xFFFF; +const pj_uint16_t PJ_IP_MULTICAST_TTL = 0xFFFF; +const pj_uint16_t PJ_IP_MULTICAST_LOOP = 0xFFFF; +const pj_uint16_t PJ_IP_ADD_MEMBERSHIP = 0xFFFF; +const pj_uint16_t PJ_IP_DROP_MEMBERSHIP = 0xFFFF; + +/* Flags */ +const int PJ_MSG_OOB = 0; +const int PJ_MSG_PEEK = KSockReadPeek; +const int PJ_MSG_DONTROUTE = 0; + +///////////////////////////////////////////////////////////////////////////// +// +// CPjSocket implementation. +// (declaration is in os_symbian.h) +// + +CPjSocket::~CPjSocket() +{ + DestroyReader(); + sock_.Close(); +} + + +// Create socket reader. +CPjSocketReader *CPjSocket::CreateReader(unsigned max_len) +{ + pj_assert(sockReader_ == NULL); + return sockReader_ = CPjSocketReader::NewL(*this, max_len); +} + +// Delete socket reader when it's not wanted. +void CPjSocket::DestroyReader() +{ + if (sockReader_) { + sockReader_->Cancel(); + delete sockReader_; + sockReader_ = NULL; + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// CPjSocketReader implementation +// (declaration in os_symbian.h) +// + + +CPjSocketReader::CPjSocketReader(CPjSocket &sock) +: CActive(EPriorityStandard), + sock_(sock), buffer_(NULL, 0), readCb_(NULL), key_(NULL) +{ +} + + +void CPjSocketReader::ConstructL(unsigned max_len) +{ + isDatagram_ = sock_.IsDatagram(); + + TUint8 *ptr = new TUint8[max_len]; + buffer_.Set(ptr, 0, (TInt)max_len); + CActiveScheduler::Add(this); +} + +CPjSocketReader *CPjSocketReader::NewL(CPjSocket &sock, unsigned max_len) +{ + CPjSocketReader *self = new (ELeave) CPjSocketReader(sock); + CleanupStack::PushL(self); + self->ConstructL(max_len); + CleanupStack::Pop(self); + + return self; +} + + +CPjSocketReader::~CPjSocketReader() +{ + const TUint8 *data = buffer_.Ptr(); + delete [] data; +} + +void CPjSocketReader::StartRecv(void (*cb)(void *key), + void *key, + TDes8 *aDesc, + TUint flags) +{ + StartRecvFrom(cb, key, aDesc, flags, NULL); +} + +void CPjSocketReader::StartRecvFrom(void (*cb)(void *key), + void *key, + TDes8 *aDesc, + TUint flags, + TSockAddr *fromAddr) +{ + readCb_ = cb; + key_ = key; + + if (aDesc == NULL) aDesc = &buffer_; + if (fromAddr == NULL) fromAddr = &recvAddr_; + + sock_.Socket().RecvFrom(*aDesc, *fromAddr, flags, iStatus); + SetActive(); +} + +void CPjSocketReader::DoCancel() +{ + sock_.Socket().CancelRecv(); +} + +void CPjSocketReader::RunL() +{ + void (*old_cb)(void *key) = readCb_; + void *old_key = key_; + + readCb_ = NULL; + key_ = NULL; + + if (old_cb) { + (*old_cb)(old_key); + } +} + +// Append data to aDesc, up to aDesc's maximum size. +// If socket is datagram based, buffer_ will be clared. +void CPjSocketReader::ReadData(TDes8 &aDesc, TInetAddr *addr) +{ + if (isDatagram_) + aDesc.Zero(); + + if (buffer_.Length() == 0) + return; + + TInt size_to_copy = aDesc.MaxLength() - aDesc.Length(); + if (size_to_copy > buffer_.Length()) + size_to_copy = buffer_.Length(); + + aDesc.Append(buffer_.Ptr(), size_to_copy); + + if (isDatagram_) + buffer_.Zero(); + else + buffer_.Delete(0, size_to_copy); + + if (addr) + *addr = recvAddr_; +} + + + +///////////////////////////////////////////////////////////////////////////// +// +// PJLIB's sock.h implementation +// + +/* + * Convert 16-bit value from network byte order to host byte order. + */ +PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort) +{ +#if PJ_IS_LITTLE_ENDIAN + return pj_swap16(netshort); +#else + return netshort; +#endif +} + +/* + * Convert 16-bit value from host byte order to network byte order. + */ +PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort) +{ +#if PJ_IS_LITTLE_ENDIAN + return pj_swap16(hostshort); +#else + return hostshort; +#endif +} + +/* + * Convert 32-bit value from network byte order to host byte order. + */ +PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong) +{ +#if PJ_IS_LITTLE_ENDIAN + return pj_swap32(netlong); +#else + return netlong; +#endif +} + +/* + * Convert 32-bit value from host byte order to network byte order. + */ +PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong) +{ +#if PJ_IS_LITTLE_ENDIAN + return pj_swap32(hostlong); +#else + return netlong; +#endif +} + +/* + * Convert an Internet host address given in network byte order + * to string in standard numbers and dots notation. + */ +PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr) +{ + static char str8[PJ_INET_ADDRSTRLEN]; + TBuf str16(0); + + /* (Symbian IP address is in host byte order) */ + TInetAddr temp_addr((TUint32)pj_ntohl(inaddr.s_addr), (TUint)0); + temp_addr.Output(str16); + + return pj_unicode_to_ansi((const wchar_t*)str16.PtrZ(), str16.Length(), + str8, sizeof(str8)); +} + +/* + * This function converts the Internet host address cp from the standard + * numbers-and-dots notation into binary data and stores it in the structure + * that inp points to. + */ +PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp) +{ + enum { MAXIPLEN = PJ_INET_ADDRSTRLEN }; + + /* Initialize output with PJ_INADDR_NONE. + * Some apps relies on this instead of the return value + * (and anyway the return value is quite confusing!) + */ + inp->s_addr = PJ_INADDR_NONE; + + /* Caution: + * this function might be called with cp->slen >= 16 + * (i.e. when called with hostname to check if it's an IP addr). + */ + PJ_ASSERT_RETURN(cp && cp->slen && inp, 0); + if (cp->slen >= 16) { + return 0; + } + + char tempaddr8[MAXIPLEN]; + pj_memcpy(tempaddr8, cp->ptr, cp->slen); + tempaddr8[cp->slen] = '\0'; + + wchar_t tempaddr16[MAXIPLEN]; + pj_ansi_to_unicode(tempaddr8, pj_ansi_strlen(tempaddr8), + tempaddr16, sizeof(tempaddr16)); + + TBuf ip_addr((const TText*)tempaddr16); + + TInetAddr addr; + addr.Init(KAfInet); + if (addr.Input(ip_addr) == KErrNone) { + /* Success (Symbian IP address is in host byte order) */ + inp->s_addr = pj_htonl(addr.Address()); + return 1; + } else { + /* Error */ + return 0; + } +} + +/* + * Convert text to IPv4/IPv6 address. + */ +PJ_DEF(pj_status_t) pj_inet_pton(int af, const pj_str_t *src, void *dst) +{ + char tempaddr[PJ_INET6_ADDRSTRLEN]; + + PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL); + PJ_ASSERT_RETURN(src && src->slen && dst, PJ_EINVAL); + + /* Initialize output with PJ_IN_ADDR_NONE for IPv4 (to be + * compatible with pj_inet_aton() + */ + if (af==PJ_AF_INET) { + ((pj_in_addr*)dst)->s_addr = PJ_INADDR_NONE; + } + + /* Caution: + * this function might be called with cp->slen >= 46 + * (i.e. when called with hostname to check if it's an IP addr). + */ + if (src->slen >= PJ_INET6_ADDRSTRLEN) { + return PJ_ENAMETOOLONG; + } + + pj_memcpy(tempaddr, src->ptr, src->slen); + tempaddr[src->slen] = '\0'; + + + wchar_t tempaddr16[PJ_INET6_ADDRSTRLEN]; + pj_ansi_to_unicode(tempaddr, pj_ansi_strlen(tempaddr), + tempaddr16, sizeof(tempaddr16)); + + TBuf ip_addr((const TText*)tempaddr16); + + TInetAddr addr; + addr.Init(KAfInet6); + if (addr.Input(ip_addr) == KErrNone) { + if (af==PJ_AF_INET) { + /* Success (Symbian IP address is in host byte order) */ + pj_uint32_t ip = pj_htonl(addr.Address()); + pj_memcpy(dst, &ip, 4); + } else if (af==PJ_AF_INET6) { + const TIp6Addr & ip6 = addr.Ip6Address(); + pj_memcpy(dst, ip6.u.iAddr8, 16); + } else { + pj_assert(!"Unexpected!"); + return PJ_EBUG; + } + return PJ_SUCCESS; + } else { + /* Error */ + return PJ_EINVAL; + } +} + +/* + * Convert IPv4/IPv6 address to text. + */ +PJ_DEF(pj_status_t) pj_inet_ntop(int af, const void *src, + char *dst, int size) + +{ + PJ_ASSERT_RETURN(src && dst && size, PJ_EINVAL); + + *dst = '\0'; + + if (af==PJ_AF_INET) { + + TBuf str16; + pj_in_addr inaddr; + + if (size < PJ_INET_ADDRSTRLEN) + return PJ_ETOOSMALL; + + pj_memcpy(&inaddr, src, 4); + + /* Symbian IP address is in host byte order */ + TInetAddr temp_addr((TUint32)pj_ntohl(inaddr.s_addr), (TUint)0); + temp_addr.Output(str16); + + pj_unicode_to_ansi((const wchar_t*)str16.PtrZ(), str16.Length(), + dst, size); + return PJ_SUCCESS; + + } else if (af==PJ_AF_INET6) { + TBuf str16; + + if (size < PJ_INET6_ADDRSTRLEN) + return PJ_ETOOSMALL; + + TIp6Addr ip6; + pj_memcpy(ip6.u.iAddr8, src, 16); + + TInetAddr temp_addr(ip6, (TUint)0); + temp_addr.Output(str16); + + pj_unicode_to_ansi((const wchar_t*)str16.PtrZ(), str16.Length(), + dst, size); + return PJ_SUCCESS; + + } else { + pj_assert(!"Unsupport address family"); + return PJ_EINVAL; + } + +} + +/* + * Get hostname. + */ +PJ_DEF(const pj_str_t*) pj_gethostname(void) +{ + static char buf[PJ_MAX_HOSTNAME]; + static pj_str_t hostname; + + PJ_CHECK_STACK(); + + if (hostname.ptr == NULL) { + RHostResolver &resv = PjSymbianOS::Instance()->GetResolver(PJ_AF_INET); + TRequestStatus reqStatus; + THostName tmpName; + + // Return empty hostname if access point is marked as down by app. + PJ_SYMBIAN_CHECK_CONNECTION2(&hostname); + + resv.GetHostName(tmpName, reqStatus); + User::WaitForRequest(reqStatus); + + hostname.ptr = pj_unicode_to_ansi((const wchar_t*)tmpName.Ptr(), tmpName.Length(), + buf, sizeof(buf)); + hostname.slen = tmpName.Length(); + } + return &hostname; +} + +/* + * Create new socket/endpoint for communication and returns a descriptor. + */ +PJ_DEF(pj_status_t) pj_sock_socket(int af, + int type, + int proto, + pj_sock_t *p_sock) +{ + TInt rc; + + PJ_CHECK_STACK(); + + /* Sanity checks. */ + PJ_ASSERT_RETURN(p_sock!=NULL, PJ_EINVAL); + + // Return failure if access point is marked as down by app. + PJ_SYMBIAN_CHECK_CONNECTION(); + + /* Set proto if none is specified. */ + if (proto == 0) { + if (type == pj_SOCK_STREAM()) + proto = KProtocolInetTcp; + else if (type == pj_SOCK_DGRAM()) + proto = KProtocolInetUdp; + } + + /* Create Symbian RSocket */ + RSocket rSock; + if (PjSymbianOS::Instance()->Connection()) + rc = rSock.Open(PjSymbianOS::Instance()->SocketServ(), + af, type, proto, + *PjSymbianOS::Instance()->Connection()); + else + rc = rSock.Open(PjSymbianOS::Instance()->SocketServ(), + af, type, proto); + + if (rc != KErrNone) + return PJ_RETURN_OS_ERROR(rc); + + + /* Wrap Symbian RSocket into PJLIB's CPjSocket, and return to caller */ + CPjSocket *pjSock = new CPjSocket(af, type, rSock); + *p_sock = (pj_sock_t)pjSock; + + return PJ_SUCCESS; +} + + +/* + * Bind socket. + */ +PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock, + const pj_sockaddr_t *addr, + int len) +{ + pj_status_t status; + TInt rc; + + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(sock != 0, PJ_EINVAL); + PJ_ASSERT_RETURN(addr && len>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL); + + // Convert PJLIB's pj_sockaddr into Symbian's TInetAddr + TInetAddr inetAddr; + status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)addr, len, inetAddr); + if (status != PJ_SUCCESS) + return status; + + // Get the RSocket instance + RSocket &rSock = ((CPjSocket*)sock)->Socket(); + + // Bind + rc = rSock.Bind(inetAddr); + + return (rc==KErrNone) ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(rc); +} + + +/* + * Bind socket. + */ +PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock, + pj_uint32_t addr32, + pj_uint16_t port) +{ + pj_sockaddr_in addr; + + PJ_CHECK_STACK(); + + pj_bzero(&addr, sizeof(addr)); + addr.sin_family = PJ_AF_INET; + addr.sin_addr.s_addr = pj_htonl(addr32); + addr.sin_port = pj_htons(port); + + return pj_sock_bind(sock, &addr, sizeof(pj_sockaddr_in)); +} + + +/* + * Close socket. + */ +PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sock) +{ + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(sock != 0, PJ_EINVAL); + + CPjSocket *pjSock = (CPjSocket*)sock; + + // This will close the socket. + delete pjSock; + + return PJ_SUCCESS; +} + +/* + * Get remote's name. + */ +PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sock, + pj_sockaddr_t *addr, + int *namelen) +{ + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(sock && addr && namelen && + *namelen>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL); + + CPjSocket *pjSock = (CPjSocket*)sock; + RSocket &rSock = pjSock->Socket(); + + // Socket must be connected. + PJ_ASSERT_RETURN(pjSock->IsConnected(), PJ_EINVALIDOP); + + TInetAddr inetAddr; + rSock.RemoteName(inetAddr); + + return PjSymbianOS::Addr2pj(inetAddr, *(pj_sockaddr*)addr, namelen); +} + +/* + * Get socket name. + */ +PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock, + pj_sockaddr_t *addr, + int *namelen) +{ + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(sock && addr && namelen && + *namelen>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL); + + CPjSocket *pjSock = (CPjSocket*)sock; + RSocket &rSock = pjSock->Socket(); + + TInetAddr inetAddr; + rSock.LocalName(inetAddr); + + return PjSymbianOS::Addr2pj(inetAddr, *(pj_sockaddr*)addr, namelen); +} + +/* + * Send data + */ +PJ_DEF(pj_status_t) pj_sock_send(pj_sock_t sock, + const void *buf, + pj_ssize_t *len, + unsigned flags) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sock && buf && len, PJ_EINVAL); + + // Return failure if access point is marked as down by app. + PJ_SYMBIAN_CHECK_CONNECTION(); + + CPjSocket *pjSock = (CPjSocket*)sock; + RSocket &rSock = pjSock->Socket(); + + // send() should only be called to connected socket + PJ_ASSERT_RETURN(pjSock->IsConnected(), PJ_EINVALIDOP); + + TPtrC8 data((const TUint8*)buf, (TInt)*len); + TRequestStatus reqStatus; + TSockXfrLength sentLen; + + rSock.Send(data, flags, reqStatus, sentLen); + User::WaitForRequest(reqStatus); + + if (reqStatus.Int()==KErrNone) { + //*len = (TInt) sentLen.Length(); + return PJ_SUCCESS; + } else + return PJ_RETURN_OS_ERROR(reqStatus.Int()); +} + + +/* + * Send data. + */ +PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock, + const void *buf, + pj_ssize_t *len, + unsigned flags, + const pj_sockaddr_t *to, + int tolen) +{ + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sock && buf && len, PJ_EINVAL); + + // Return failure if access point is marked as down by app. + PJ_SYMBIAN_CHECK_CONNECTION(); + + CPjSocket *pjSock = (CPjSocket*)sock; + RSocket &rSock = pjSock->Socket(); + + // Only supports AF_INET for now + PJ_ASSERT_RETURN(tolen>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL); + + TInetAddr inetAddr; + status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)to, tolen, inetAddr); + if (status != PJ_SUCCESS) + return status; + + TPtrC8 data((const TUint8*)buf, (TInt)*len); + TRequestStatus reqStatus; + TSockXfrLength sentLen; + + rSock.SendTo(data, inetAddr, flags, reqStatus, sentLen); + User::WaitForRequest(reqStatus); + + if (reqStatus.Int()==KErrNone) { + //For some reason TSockXfrLength is not returning correctly! + //*len = (TInt) sentLen.Length(); + return PJ_SUCCESS; + } else + return PJ_RETURN_OS_ERROR(reqStatus.Int()); +} + +/* + * Receive data. + */ +PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock, + void *buf, + pj_ssize_t *len, + unsigned flags) +{ + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(sock && buf && len, PJ_EINVAL); + PJ_ASSERT_RETURN(*len > 0, PJ_EINVAL); + + // Return failure if access point is marked as down by app. + PJ_SYMBIAN_CHECK_CONNECTION(); + + CPjSocket *pjSock = (CPjSocket*)sock; + + if (pjSock->Reader()) { + CPjSocketReader *reader = pjSock->Reader(); + + while (reader->IsActive() && !reader->HasData()) { + User::WaitForAnyRequest(); + } + + if (reader->HasData()) { + TPtr8 data((TUint8*)buf, (TInt)*len); + TInetAddr inetAddr; + + reader->ReadData(data, &inetAddr); + + *len = data.Length(); + return PJ_SUCCESS; + } + } + + TRequestStatus reqStatus; + TSockXfrLength recvLen; + TPtr8 data((TUint8*)buf, (TInt)*len, (TInt)*len); + + if (pjSock->IsDatagram()) { + pjSock->Socket().Recv(data, flags, reqStatus); + } else { + // Using static like this is not pretty, but we don't need to use + // the value anyway, hence doing it like this is probably most + // optimal. + static TSockXfrLength len; + pjSock->Socket().RecvOneOrMore(data, flags, reqStatus, len); + } + User::WaitForRequest(reqStatus); + + if (reqStatus == KErrNone) { + //*len = (TInt)recvLen.Length(); + *len = data.Length(); + return PJ_SUCCESS; + } else { + *len = -1; + return PJ_RETURN_OS_ERROR(reqStatus.Int()); + } +} + +/* + * Receive data. + */ +PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock, + void *buf, + pj_ssize_t *len, + unsigned flags, + pj_sockaddr_t *from, + int *fromlen) +{ + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(sock && buf && len && from && fromlen, PJ_EINVAL); + PJ_ASSERT_RETURN(*len > 0, PJ_EINVAL); + PJ_ASSERT_RETURN(*fromlen >= (int)sizeof(pj_sockaddr_in), PJ_EINVAL); + + // Return failure if access point is marked as down by app. + PJ_SYMBIAN_CHECK_CONNECTION(); + + CPjSocket *pjSock = (CPjSocket*)sock; + RSocket &rSock = pjSock->Socket(); + + if (pjSock->Reader()) { + CPjSocketReader *reader = pjSock->Reader(); + + while (reader->IsActive() && !reader->HasData()) { + User::WaitForAnyRequest(); + } + + if (reader->HasData()) { + TPtr8 data((TUint8*)buf, (TInt)*len); + TInetAddr inetAddr; + + reader->ReadData(data, &inetAddr); + + *len = data.Length(); + + if (from && fromlen) { + return PjSymbianOS::Addr2pj(inetAddr, *(pj_sockaddr*)from, + fromlen); + } else { + return PJ_SUCCESS; + } + } + } + + TInetAddr inetAddr; + TRequestStatus reqStatus; + TSockXfrLength recvLen; + TPtr8 data((TUint8*)buf, (TInt)*len, (TInt)*len); + + rSock.RecvFrom(data, inetAddr, flags, reqStatus, recvLen); + User::WaitForRequest(reqStatus); + + if (reqStatus == KErrNone) { + //*len = (TInt)recvLen.Length(); + *len = data.Length(); + return PjSymbianOS::Addr2pj(inetAddr, *(pj_sockaddr*)from, fromlen); + } else { + *len = -1; + *fromlen = -1; + return PJ_RETURN_OS_ERROR(reqStatus.Int()); + } +} + +/* + * Get socket option. + */ +PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sock, + pj_uint16_t level, + pj_uint16_t optname, + void *optval, + int *optlen) +{ + // Not supported for now. + PJ_UNUSED_ARG(sock); + PJ_UNUSED_ARG(level); + PJ_UNUSED_ARG(optname); + PJ_UNUSED_ARG(optval); + PJ_UNUSED_ARG(optlen); + return PJ_EINVALIDOP; +} + +/* + * Set socket option. + */ +PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock, + pj_uint16_t level, + pj_uint16_t optname, + const void *optval, + int optlen) +{ + // Not supported for now. + PJ_UNUSED_ARG(sock); + PJ_UNUSED_ARG(level); + PJ_UNUSED_ARG(optname); + PJ_UNUSED_ARG(optval); + PJ_UNUSED_ARG(optlen); + return PJ_EINVALIDOP; +} + +/* + * Connect socket. + */ +PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sock, + const pj_sockaddr_t *addr, + int namelen) +{ + pj_status_t status; + + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(sock && addr && namelen, PJ_EINVAL); + PJ_ASSERT_RETURN(((pj_sockaddr*)addr)->addr.sa_family == PJ_AF_INET, + PJ_EINVAL); + + // Return failure if access point is marked as down by app. + PJ_SYMBIAN_CHECK_CONNECTION(); + + CPjSocket *pjSock = (CPjSocket*)sock; + RSocket &rSock = pjSock->Socket(); + + TInetAddr inetAddr; + TRequestStatus reqStatus; + + status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)addr, namelen, inetAddr); + if (status != PJ_SUCCESS) + return status; + + rSock.Connect(inetAddr, reqStatus); + User::WaitForRequest(reqStatus); + + if (reqStatus == KErrNone) { + pjSock->SetConnected(true); + return PJ_SUCCESS; + } else { + return PJ_RETURN_OS_ERROR(reqStatus.Int()); + } +} + + +/* + * Shutdown socket. + */ +#if PJ_HAS_TCP +PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sock, + int how) +{ + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(sock, PJ_EINVAL); + + CPjSocket *pjSock = (CPjSocket*)sock; + RSocket &rSock = pjSock->Socket(); + + RSocket::TShutdown aHow; + if (how == PJ_SD_RECEIVE) + aHow = RSocket::EStopInput; + else if (how == PJ_SHUT_WR) + aHow = RSocket::EStopOutput; + else + aHow = RSocket::ENormal; + + TRequestStatus reqStatus; + + rSock.Shutdown(aHow, reqStatus); + User::WaitForRequest(reqStatus); + + if (reqStatus == KErrNone) { + return PJ_SUCCESS; + } else { + return PJ_RETURN_OS_ERROR(reqStatus.Int()); + } +} + +/* + * Start listening to incoming connections. + */ +PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sock, + int backlog) +{ + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(sock && backlog, PJ_EINVAL); + + CPjSocket *pjSock = (CPjSocket*)sock; + RSocket &rSock = pjSock->Socket(); + + TInt rc = rSock.Listen((TUint)backlog); + + if (rc == KErrNone) { + return PJ_SUCCESS; + } else { + return PJ_RETURN_OS_ERROR(rc); + } +} + +/* + * Accept incoming connections + */ +PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd, + pj_sock_t *newsock, + pj_sockaddr_t *addr, + int *addrlen) +{ + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(serverfd && newsock, PJ_EINVAL); + + CPjSocket *pjSock = (CPjSocket*)serverfd; + RSocket &rSock = pjSock->Socket(); + + // Create a 'blank' socket + RSocket newSock; + newSock.Open(PjSymbianOS::Instance()->SocketServ()); + + // Call Accept() + TRequestStatus reqStatus; + + rSock.Accept(newSock, reqStatus); + User::WaitForRequest(reqStatus); + + if (reqStatus != KErrNone) { + return PJ_RETURN_OS_ERROR(reqStatus.Int()); + } + + // Create PJ socket + CPjSocket *newPjSock = new CPjSocket(pjSock->GetAf(), pjSock->GetSockType(), + newSock); + newPjSock->SetConnected(true); + + *newsock = (pj_sock_t) newPjSock; + + if (addr && addrlen) { + return pj_sock_getpeername(*newsock, addr, addrlen); + } + + return PJ_SUCCESS; +} +#endif /* PJ_HAS_TCP */ + + diff --git a/pjlib/src/pj/ssl_sock_common.c b/pjlib/src/pj/ssl_sock_common.c new file mode 100644 index 0000000..768a640 --- /dev/null +++ b/pjlib/src/pj/ssl_sock_common.c @@ -0,0 +1,138 @@ +/* $Id: ssl_sock_common.c 3999 2012-03-30 07:10:13Z bennylp $ */ +/* + * Copyright (C) 2009-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 +#include +#include +#include + +/* + * Initialize the SSL socket configuration with the default values. + */ +PJ_DEF(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param) +{ + pj_bzero(param, sizeof(*param)); + + /* Socket config */ + param->sock_af = PJ_AF_INET; + param->sock_type = pj_SOCK_STREAM(); + param->async_cnt = 1; + param->concurrency = -1; + param->whole_data = PJ_TRUE; + param->send_buffer_size = 8192; +#if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0 + param->read_buffer_size = 1500; +#endif + param->qos_type = PJ_QOS_TYPE_BEST_EFFORT; + param->qos_ignore_error = PJ_TRUE; + + /* Security config */ + param->proto = PJ_SSL_SOCK_PROTO_DEFAULT; +} + + +PJ_DEF(pj_status_t) pj_ssl_cert_get_verify_status_strings( + pj_uint32_t verify_status, + const char *error_strings[], + unsigned *count) +{ + unsigned i = 0, shift_idx = 0; + unsigned unknown = 0; + pj_uint32_t errs; + + PJ_ASSERT_RETURN(error_strings && count, PJ_EINVAL); + + if (verify_status == PJ_SSL_CERT_ESUCCESS && *count) { + error_strings[0] = "OK"; + *count = 1; + return PJ_SUCCESS; + } + + errs = verify_status; + + while (errs && i < *count) { + pj_uint32_t err; + const char *p = NULL; + + if ((errs & 1) == 0) { + shift_idx++; + errs >>= 1; + continue; + } + + err = (1 << shift_idx); + + switch (err) { + case PJ_SSL_CERT_EISSUER_NOT_FOUND: + p = "The issuer certificate cannot be found"; + break; + case PJ_SSL_CERT_EUNTRUSTED: + p = "The certificate is untrusted"; + break; + case PJ_SSL_CERT_EVALIDITY_PERIOD: + p = "The certificate has expired or not yet valid"; + break; + case PJ_SSL_CERT_EINVALID_FORMAT: + p = "One or more fields of the certificate cannot be decoded " + "due to invalid format"; + break; + case PJ_SSL_CERT_EISSUER_MISMATCH: + p = "The issuer info in the certificate does not match to the " + "(candidate) issuer certificate"; + break; + case PJ_SSL_CERT_ECRL_FAILURE: + p = "The CRL certificate cannot be found or cannot be read " + "properly"; + break; + case PJ_SSL_CERT_EREVOKED: + p = "The certificate has been revoked"; + break; + case PJ_SSL_CERT_EINVALID_PURPOSE: + p = "The certificate or CA certificate cannot be used for the " + "specified purpose"; + break; + case PJ_SSL_CERT_ECHAIN_TOO_LONG: + p = "The certificate chain length is too long"; + break; + case PJ_SSL_CERT_EIDENTITY_NOT_MATCH: + p = "The server identity does not match to any identities " + "specified in the certificate"; + break; + case PJ_SSL_CERT_EUNKNOWN: + default: + unknown++; + break; + } + + /* Set error string */ + if (p) + error_strings[i++] = p; + + /* Next */ + shift_idx++; + errs >>= 1; + } + + /* Unknown error */ + if (unknown && i < *count) + error_strings[i++] = "Unknown verification error"; + + *count = i; + + return PJ_SUCCESS; +} diff --git a/pjlib/src/pj/ssl_sock_dump.c b/pjlib/src/pj/ssl_sock_dump.c new file mode 100644 index 0000000..118992e --- /dev/null +++ b/pjlib/src/pj/ssl_sock_dump.c @@ -0,0 +1,148 @@ +/* $Id: ssl_sock_dump.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2009-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 +#include +#include +#include + + +/* Only build when PJ_HAS_SSL_SOCK is enabled */ +#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK!=0 + +#define THIS_FILE "ssl_sock_dump.c" + +#define CHECK_BUF_LEN() \ + if ((len < 0) || (len >= end-p)) { \ + *p = '\0'; \ + return -1; \ + } \ + p += len; + +PJ_DEF(pj_ssize_t) pj_ssl_cert_info_dump(const pj_ssl_cert_info *ci, + const char *indent, + char *buf, + pj_size_t buf_size) +{ + const char *wdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + pj_parsed_time pt1; + pj_parsed_time pt2; + unsigned i; + int len = 0; + char *p, *end; + + p = buf; + end = buf + buf_size; + + pj_time_decode(&ci->validity.start, &pt1); + pj_time_decode(&ci->validity.end, &pt2); + + /* Version */ + len = pj_ansi_snprintf(p, end-p, "%sVersion : v%d\n", + indent, ci->version); + CHECK_BUF_LEN(); + + /* Serial number */ + len = pj_ansi_snprintf(p, end-p, "%sSerial : ", indent); + CHECK_BUF_LEN(); + + for (i = 0; i < sizeof(ci->serial_no) && !ci->serial_no[i]; ++i); + for (; i < sizeof(ci->serial_no); ++i) { + len = pj_ansi_snprintf(p, end-p, "%02X ", ci->serial_no[i]); + CHECK_BUF_LEN(); + } + *(p-1) = '\n'; + + /* Subject */ + len = pj_ansi_snprintf( p, end-p, "%sSubject : %.*s\n", indent, + (int)ci->subject.cn.slen, + ci->subject.cn.ptr); + CHECK_BUF_LEN(); + len = pj_ansi_snprintf( p, end-p, "%s %.*s\n", indent, + (int)ci->subject.info.slen, + ci->subject.info.ptr); + CHECK_BUF_LEN(); + + /* Issuer */ + len = pj_ansi_snprintf( p, end-p, "%sIssuer : %.*s\n", indent, + (int)ci->issuer.cn.slen, + ci->issuer.cn.ptr); + CHECK_BUF_LEN(); + len = pj_ansi_snprintf( p, end-p, "%s %.*s\n", indent, + (int)ci->issuer.info.slen, + ci->issuer.info.ptr); + CHECK_BUF_LEN(); + + /* Validity period */ + len = pj_ansi_snprintf( p, end-p, "%sValid from : %s %4d-%02d-%02d " + "%02d:%02d:%02d.%03d %s\n", indent, + wdays[pt1.wday], pt1.year, pt1.mon+1, pt1.day, + pt1.hour, pt1.min, pt1.sec, pt1.msec, + (ci->validity.gmt? "GMT":"")); + CHECK_BUF_LEN(); + + len = pj_ansi_snprintf( p, end-p, "%sValid to : %s %4d-%02d-%02d " + "%02d:%02d:%02d.%03d %s\n", indent, + wdays[pt2.wday], pt2.year, pt2.mon+1, pt2.day, + pt2.hour, pt2.min, pt2.sec, pt2.msec, + (ci->validity.gmt? "GMT":"")); + CHECK_BUF_LEN(); + + /* Subject alternative name extension */ + if (ci->subj_alt_name.cnt) { + unsigned i; + + len = pj_ansi_snprintf(p, end-p, "%ssubjectAltName extension\n", + indent); + CHECK_BUF_LEN(); + + for (i = 0; i < ci->subj_alt_name.cnt; ++i) { + const char *type = NULL; + + switch(ci->subj_alt_name.entry[i].type) { + case PJ_SSL_CERT_NAME_RFC822: + type = "MAIL"; + break; + case PJ_SSL_CERT_NAME_DNS: + type = " DNS"; + break; + case PJ_SSL_CERT_NAME_URI: + type = " URI"; + break; + case PJ_SSL_CERT_NAME_IP: + type = " IP"; + break; + default: + break; + } + if (type) { + len = pj_ansi_snprintf( p, end-p, "%s %s : %.*s\n", indent, + type, + (int)ci->subj_alt_name.entry[i].name.slen, + ci->subj_alt_name.entry[i].name.ptr); + CHECK_BUF_LEN(); + } + } + } + + return (p-buf); +} + + +#endif /* PJ_HAS_SSL_SOCK */ + diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c new file mode 100644 index 0000000..82ad2a5 --- /dev/null +++ b/pjlib/src/pj/ssl_sock_ossl.c @@ -0,0 +1,2414 @@ +/* $Id: ssl_sock_ossl.c 4146 2012-05-30 06:35:59Z nanang $ */ +/* + * Copyright (C) 2009-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Only build when PJ_HAS_SSL_SOCK is enabled */ +#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK!=0 + +#define THIS_FILE "ssl_sock_ossl.c" + +/* Workaround for ticket #985 */ +#define DELAYED_CLOSE_TIMEOUT 200 + +/* Maximum ciphers */ +#define MAX_CIPHERS 100 + +/* + * Include OpenSSL headers + */ +#include +#include +#include +#include + + +#ifdef _MSC_VER +# ifdef _DEBUG +# pragma comment( lib, "libeay32MTd") +# pragma comment( lib, "ssleay32MTd") +#else +# pragma comment( lib, "libeay32MT") +# pragma comment( lib, "ssleay32MT") +# endif +#endif + + +/* + * SSL/TLS state enumeration. + */ +enum ssl_state { + SSL_STATE_NULL, + SSL_STATE_HANDSHAKING, + SSL_STATE_ESTABLISHED +}; + +/* + * Internal timer types. + */ +enum timer_id +{ + TIMER_NONE, + TIMER_HANDSHAKE_TIMEOUT, + TIMER_CLOSE +}; + +/* + * Structure of SSL socket read buffer. + */ +typedef struct read_data_t +{ + void *data; + pj_size_t len; +} read_data_t; + +/* + * Get the offset of pointer to read-buffer of SSL socket from read-buffer + * of active socket. Note that both SSL socket and active socket employ + * different but correlated read-buffers (as much as async_cnt for each), + * and to make it easier/faster to find corresponding SSL socket's read-buffer + * from known active socket's read-buffer, the pointer of corresponding + * SSL socket's read-buffer is stored right after the end of active socket's + * read-buffer. + */ +#define OFFSET_OF_READ_DATA_PTR(ssock, asock_rbuf) \ + (read_data_t**) \ + ((pj_int8_t*)(asock_rbuf) + \ + ssock->param.read_buffer_size) + +/* + * Structure of SSL socket write buffer. + */ +typedef struct write_data_t { + pj_ioqueue_op_key_t key; + pj_size_t record_len; + pj_ioqueue_op_key_t *app_key; + pj_size_t plain_data_len; + pj_size_t data_len; + unsigned flags; + union { + char content[1]; + const char *ptr; + } data; +} write_data_t; + +/* + * Structure of SSL socket write state. + */ +typedef struct write_state_t { + char *buf; + pj_size_t max_len; + char *start; + pj_size_t len; + write_data_t *last_data; +} write_state_t; + +/* + * Structure of write data pending. + */ +typedef struct write_pending_t { + PJ_DECL_LIST_MEMBER(struct write_pending_t); + write_data_t data; +} write_pending_t; + +/* + * Secure socket structure definition. + */ +struct pj_ssl_sock_t +{ + pj_pool_t *pool; + pj_ssl_sock_t *parent; + pj_ssl_sock_param param; + pj_ssl_cert_t *cert; + + pj_ssl_cert_info local_cert_info; + pj_ssl_cert_info remote_cert_info; + + pj_bool_t is_server; + enum ssl_state ssl_state; + pj_ioqueue_op_key_t handshake_op_key; + pj_timer_entry timer; + pj_status_t verify_status; + + unsigned long last_err; + + pj_sock_t sock; + pj_activesock_t *asock; + + pj_sockaddr local_addr; + pj_sockaddr rem_addr; + int addr_len; + + pj_bool_t read_started; + pj_size_t read_size; + pj_uint32_t read_flags; + void **asock_rbuf; + read_data_t *ssock_rbuf; + + write_state_t write_state; + write_pending_t write_pending; + write_pending_t write_pending_empty; + pj_lock_t *write_mutex; /* protect write BIO and write_state */ + + SSL_CTX *ossl_ctx; + SSL *ossl_ssl; + BIO *ossl_rbio; + BIO *ossl_wbio; +}; + + +/* + * Certificate/credential structure definition. + */ +struct pj_ssl_cert_t +{ + pj_str_t CA_file; + pj_str_t cert_file; + pj_str_t privkey_file; + pj_str_t privkey_pass; +}; + + +static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock); + +/* + ******************************************************************* + * Static/internal functions. + ******************************************************************* + */ + +/** + * Mapping from OpenSSL error codes to pjlib error space. + */ + +#define PJ_SSL_ERRNO_START (PJ_ERRNO_START_USER + \ + PJ_ERRNO_SPACE_SIZE*6) + +#define PJ_SSL_ERRNO_SPACE_SIZE PJ_ERRNO_SPACE_SIZE + +/* Expected maximum value of reason component in OpenSSL error code */ +#define MAX_OSSL_ERR_REASON 1200 + +static pj_status_t STATUS_FROM_SSL_ERR(pj_ssl_sock_t *ssock, + unsigned long err) +{ + pj_status_t status; + + /* General SSL error, dig more from OpenSSL error queue */ + if (err == SSL_ERROR_SSL) + err = ERR_get_error(); + + /* OpenSSL error range is much wider than PJLIB errno space, so + * if it exceeds the space, only the error reason will be kept. + * Note that the last native error will be kept as is and can be + * retrieved via SSL socket info. + */ + status = ERR_GET_LIB(err)*MAX_OSSL_ERR_REASON + ERR_GET_REASON(err); + if (status > PJ_SSL_ERRNO_SPACE_SIZE) + status = ERR_GET_REASON(err); + + status += PJ_SSL_ERRNO_START; + ssock->last_err = err; + return status; +} + +static pj_status_t GET_SSL_STATUS(pj_ssl_sock_t *ssock) +{ + return STATUS_FROM_SSL_ERR(ssock, ERR_get_error()); +} + + +/* + * Get error string of OpenSSL. + */ +static pj_str_t ssl_strerror(pj_status_t status, + char *buf, pj_size_t bufsize) +{ + pj_str_t errstr; + unsigned long ssl_err = status; + + if (ssl_err) { + unsigned long l, r; + ssl_err -= PJ_SSL_ERRNO_START; + l = ssl_err / MAX_OSSL_ERR_REASON; + r = ssl_err % MAX_OSSL_ERR_REASON; + ssl_err = ERR_PACK(l, 0, r); + } + +#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) + + { + const char *tmp = NULL; + tmp = ERR_reason_error_string(ssl_err); + if (tmp) { + pj_ansi_strncpy(buf, tmp, bufsize); + errstr = pj_str(buf); + return errstr; + } + } + +#endif /* PJ_HAS_ERROR_STRING */ + + errstr.ptr = buf; + errstr.slen = pj_ansi_snprintf(buf, bufsize, + "Unknown OpenSSL error %lu", + ssl_err); + + return errstr; +} + + +/* OpenSSL library initialization counter */ +static int openssl_init_count; + +/* OpenSSL available ciphers */ +static unsigned openssl_cipher_num; +static struct openssl_ciphers_t { + pj_ssl_cipher id; + const char *name; +} openssl_ciphers[MAX_CIPHERS]; + +/* OpenSSL application data index */ +static int sslsock_idx; + + +/* Initialize OpenSSL */ +static pj_status_t init_openssl(void) +{ + pj_status_t status; + + if (openssl_init_count) + return PJ_SUCCESS; + + openssl_init_count = 1; + + /* Register error subsystem */ + status = pj_register_strerror(PJ_SSL_ERRNO_START, + PJ_SSL_ERRNO_SPACE_SIZE, + &ssl_strerror); + pj_assert(status == PJ_SUCCESS); + + /* Init OpenSSL lib */ + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + + /* Init available ciphers */ + if (openssl_cipher_num == 0) { + SSL_METHOD *meth = NULL; + SSL_CTX *ctx; + SSL *ssl; + STACK_OF(SSL_CIPHER) *sk_cipher; + unsigned i, n; + + meth = (SSL_METHOD*)SSLv23_server_method(); + if (!meth) + meth = (SSL_METHOD*)TLSv1_server_method(); + if (!meth) + meth = (SSL_METHOD*)SSLv3_server_method(); +#ifndef OPENSSL_NO_SSL2 + if (!meth) + meth = (SSL_METHOD*)SSLv2_server_method(); +#endif + pj_assert(meth); + + ctx=SSL_CTX_new(meth); + SSL_CTX_set_cipher_list(ctx, "ALL"); + + ssl = SSL_new(ctx); + sk_cipher = SSL_get_ciphers(ssl); + + n = sk_SSL_CIPHER_num(sk_cipher); + if (n > PJ_ARRAY_SIZE(openssl_ciphers)) + n = PJ_ARRAY_SIZE(openssl_ciphers); + + for (i = 0; i < n; ++i) { + SSL_CIPHER *c; + c = sk_SSL_CIPHER_value(sk_cipher,i); + openssl_ciphers[i].id = (pj_ssl_cipher) + (pj_uint32_t)c->id & 0x00FFFFFF; + openssl_ciphers[i].name = SSL_CIPHER_get_name(c); + } + + SSL_free(ssl); + SSL_CTX_free(ctx); + + openssl_cipher_num = n; + } + + /* Create OpenSSL application data index for SSL socket */ + sslsock_idx = SSL_get_ex_new_index(0, "SSL socket", NULL, NULL, NULL); + + return PJ_SUCCESS; +} + + +/* Shutdown OpenSSL */ +static void shutdown_openssl(void) +{ + PJ_UNUSED_ARG(openssl_init_count); +} + + +/* SSL password callback. */ +static int password_cb(char *buf, int num, int rwflag, void *user_data) +{ + pj_ssl_cert_t *cert = (pj_ssl_cert_t*) user_data; + + PJ_UNUSED_ARG(rwflag); + + if(num < cert->privkey_pass.slen) + return 0; + + pj_memcpy(buf, cert->privkey_pass.ptr, cert->privkey_pass.slen); + return cert->privkey_pass.slen; +} + + +/* SSL password callback. */ +static int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + pj_ssl_sock_t *ssock; + SSL *ossl_ssl; + int err; + + /* Get SSL instance */ + ossl_ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + pj_assert(ossl_ssl); + + /* Get SSL socket instance */ + ssock = SSL_get_ex_data(ossl_ssl, sslsock_idx); + pj_assert(ssock); + + /* Store verification status */ + err = X509_STORE_CTX_get_error(x509_ctx); + switch (err) { + case X509_V_OK: + break; + + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + ssock->verify_status |= PJ_SSL_CERT_EISSUER_NOT_FOUND; + break; + + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: + case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT; + break; + + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CERT_HAS_EXPIRED: + ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD; + break; + + case X509_V_ERR_UNABLE_TO_GET_CRL: + case X509_V_ERR_CRL_NOT_YET_VALID: + case X509_V_ERR_CRL_HAS_EXPIRED: + case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: + case X509_V_ERR_CRL_SIGNATURE_FAILURE: + case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: + case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: + ssock->verify_status |= PJ_SSL_CERT_ECRL_FAILURE; + break; + + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + case X509_V_ERR_CERT_UNTRUSTED: + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED; + break; + + case X509_V_ERR_CERT_SIGNATURE_FAILURE: + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: + case X509_V_ERR_AKID_SKID_MISMATCH: + case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: + case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: + ssock->verify_status |= PJ_SSL_CERT_EISSUER_MISMATCH; + break; + + case X509_V_ERR_CERT_REVOKED: + ssock->verify_status |= PJ_SSL_CERT_EREVOKED; + break; + + case X509_V_ERR_INVALID_PURPOSE: + case X509_V_ERR_CERT_REJECTED: + case X509_V_ERR_INVALID_CA: + ssock->verify_status |= PJ_SSL_CERT_EINVALID_PURPOSE; + break; + + case X509_V_ERR_CERT_CHAIN_TOO_LONG: /* not really used */ + case X509_V_ERR_PATH_LENGTH_EXCEEDED: + ssock->verify_status |= PJ_SSL_CERT_ECHAIN_TOO_LONG; + break; + + /* Unknown errors */ + case X509_V_ERR_OUT_OF_MEM: + default: + ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN; + break; + } + + /* When verification is not requested just return ok here, however + * application can still get the verification status. + */ + if (PJ_FALSE == ssock->param.verify_peer) + preverify_ok = 1; + + return preverify_ok; +} + +/* Setting SSL sock cipher list */ +static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock); + + +/* Create and initialize new SSL context and instance */ +static pj_status_t create_ssl(pj_ssl_sock_t *ssock) +{ + SSL_METHOD *ssl_method; + SSL_CTX *ctx; + pj_ssl_cert_t *cert; + int mode, rc; + pj_status_t status; + + pj_assert(ssock); + + cert = ssock->cert; + + /* Make sure OpenSSL library has been initialized */ + init_openssl(); + + /* Determine SSL method to use */ + switch (ssock->param.proto) { + case PJ_SSL_SOCK_PROTO_DEFAULT: + case PJ_SSL_SOCK_PROTO_TLS1: + ssl_method = (SSL_METHOD*)TLSv1_method(); + break; +#ifndef OPENSSL_NO_SSL2 + case PJ_SSL_SOCK_PROTO_SSL2: + ssl_method = (SSL_METHOD*)SSLv2_method(); + break; +#endif + case PJ_SSL_SOCK_PROTO_SSL3: + ssl_method = (SSL_METHOD*)SSLv3_method(); + break; + case PJ_SSL_SOCK_PROTO_SSL23: + ssl_method = (SSL_METHOD*)SSLv23_method(); + break; + //case PJ_SSL_SOCK_PROTO_DTLS1: + //ssl_method = (SSL_METHOD*)DTLSv1_method(); + //break; + default: + return PJ_EINVAL; + } + + /* Create SSL context */ + ctx = SSL_CTX_new(ssl_method); + if (ctx == NULL) { + return GET_SSL_STATUS(ssock); + } + + /* Apply credentials */ + if (cert) { + /* Load CA list if one is specified. */ + if (cert->CA_file.slen) { + + rc = SSL_CTX_load_verify_locations(ctx, cert->CA_file.ptr, NULL); + + if (rc != 1) { + status = GET_SSL_STATUS(ssock); + PJ_LOG(1,(ssock->pool->obj_name, "Error loading CA list file " + "'%s'", cert->CA_file.ptr)); + SSL_CTX_free(ctx); + return status; + } + } + + /* Set password callback */ + if (cert->privkey_pass.slen) { + SSL_CTX_set_default_passwd_cb(ctx, password_cb); + SSL_CTX_set_default_passwd_cb_userdata(ctx, cert); + } + + + /* Load certificate if one is specified */ + if (cert->cert_file.slen) { + + /* Load certificate chain from file into ctx */ + rc = SSL_CTX_use_certificate_chain_file(ctx, cert->cert_file.ptr); + + if(rc != 1) { + status = GET_SSL_STATUS(ssock); + PJ_LOG(1,(ssock->pool->obj_name, "Error loading certificate " + "chain file '%s'", cert->cert_file.ptr)); + SSL_CTX_free(ctx); + return status; + } + } + + + /* Load private key if one is specified */ + if (cert->privkey_file.slen) { + /* Adds the first private key found in file to ctx */ + rc = SSL_CTX_use_PrivateKey_file(ctx, cert->privkey_file.ptr, + SSL_FILETYPE_PEM); + + if(rc != 1) { + status = GET_SSL_STATUS(ssock); + PJ_LOG(1,(ssock->pool->obj_name, "Error adding private key " + "from '%s'", cert->privkey_file.ptr)); + SSL_CTX_free(ctx); + return status; + } + } + } + + /* Create SSL instance */ + ssock->ossl_ctx = ctx; + ssock->ossl_ssl = SSL_new(ssock->ossl_ctx); + if (ssock->ossl_ssl == NULL) { + return GET_SSL_STATUS(ssock); + } + + /* Set SSL sock as application data of SSL instance */ + SSL_set_ex_data(ssock->ossl_ssl, sslsock_idx, ssock); + + /* SSL verification options */ + mode = SSL_VERIFY_PEER; + if (ssock->is_server && ssock->param.require_client_cert) + mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + + SSL_set_verify(ssock->ossl_ssl, mode, &verify_cb); + + /* Set cipher list */ + status = set_cipher_list(ssock); + if (status != PJ_SUCCESS) + return status; + + /* Setup SSL BIOs */ + ssock->ossl_rbio = BIO_new(BIO_s_mem()); + ssock->ossl_wbio = BIO_new(BIO_s_mem()); + BIO_set_close(ssock->ossl_rbio, BIO_CLOSE); + BIO_set_close(ssock->ossl_wbio, BIO_CLOSE); + SSL_set_bio(ssock->ossl_ssl, ssock->ossl_rbio, ssock->ossl_wbio); + + return PJ_SUCCESS; +} + + +/* Destroy SSL context and instance */ +static void destroy_ssl(pj_ssl_sock_t *ssock) +{ + /* Destroy SSL instance */ + if (ssock->ossl_ssl) { + SSL_shutdown(ssock->ossl_ssl); + SSL_free(ssock->ossl_ssl); /* this will also close BIOs */ + ssock->ossl_ssl = NULL; + } + + /* Destroy SSL context */ + if (ssock->ossl_ctx) { + SSL_CTX_free(ssock->ossl_ctx); + ssock->ossl_ctx = NULL; + } + + /* Potentially shutdown OpenSSL library if this is the last + * context exists. + */ + shutdown_openssl(); +} + + +/* Reset SSL socket state */ +static void reset_ssl_sock_state(pj_ssl_sock_t *ssock) +{ + ssock->ssl_state = SSL_STATE_NULL; + + destroy_ssl(ssock); + + if (ssock->asock) { + pj_activesock_close(ssock->asock); + ssock->asock = NULL; + ssock->sock = PJ_INVALID_SOCKET; + } + if (ssock->sock != PJ_INVALID_SOCKET) { + pj_sock_close(ssock->sock); + ssock->sock = PJ_INVALID_SOCKET; + } + + /* Upon error, OpenSSL may leave any error description in the thread + * error queue, which sometime may cause next call to SSL API returning + * false error alarm, e.g: in Linux, SSL_CTX_use_certificate_chain_file() + * returning false error after a handshake error (in different SSL_CTX!). + * For now, just clear thread error queue here. + */ + ERR_clear_error(); +} + + +/* Generate cipher list with user preference order in OpenSSL format */ +static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock) +{ + char buf[1024]; + pj_str_t cipher_list; + STACK_OF(SSL_CIPHER) *sk_cipher; + unsigned i; + int j, ret; + + if (ssock->param.ciphers_num == 0) + return PJ_SUCCESS; + + pj_strset(&cipher_list, buf, 0); + + /* Set SSL with ALL available ciphers */ + SSL_set_cipher_list(ssock->ossl_ssl, "ALL"); + + /* Generate user specified cipher list in OpenSSL format */ + sk_cipher = SSL_get_ciphers(ssock->ossl_ssl); + for (i = 0; i < ssock->param.ciphers_num; ++i) { + for (j = 0; j < sk_SSL_CIPHER_num(sk_cipher); ++j) { + SSL_CIPHER *c; + c = sk_SSL_CIPHER_value(sk_cipher, j); + if (ssock->param.ciphers[i] == (pj_ssl_cipher) + ((pj_uint32_t)c->id & 0x00FFFFFF)) + { + const char *c_name; + + c_name = SSL_CIPHER_get_name(c); + + /* Check buffer size */ + if (cipher_list.slen + pj_ansi_strlen(c_name) + 2 > sizeof(buf)) { + pj_assert(!"Insufficient temporary buffer for cipher"); + return PJ_ETOOMANY; + } + + /* Add colon separator */ + if (cipher_list.slen) + pj_strcat2(&cipher_list, ":"); + + /* Add the cipher */ + pj_strcat2(&cipher_list, c_name); + break; + } + } + } + + /* Put NULL termination in the generated cipher list */ + cipher_list.ptr[cipher_list.slen] = '\0'; + + /* Finally, set chosen cipher list */ + ret = SSL_set_cipher_list(ssock->ossl_ssl, buf); + if (ret < 1) { + return GET_SSL_STATUS(ssock); + } + + return PJ_SUCCESS; +} + + +/* Parse OpenSSL ASN1_TIME to pj_time_val and GMT info */ +static pj_bool_t parse_ossl_asn1_time(pj_time_val *tv, pj_bool_t *gmt, + const ASN1_TIME *tm) +{ + unsigned long parts[7] = {0}; + char *p, *end; + unsigned len; + pj_bool_t utc; + pj_parsed_time pt; + int i; + + utc = tm->type == V_ASN1_UTCTIME; + p = (char*)tm->data; + len = tm->length; + end = p + len - 1; + + /* GMT */ + *gmt = (*end == 'Z'); + + /* parse parts */ + for (i = 0; i < 7 && p < end; ++i) { + pj_str_t st; + + if (i==0 && !utc) { + /* 4 digits year part for non-UTC time format */ + st.slen = 4; + } else if (i==6) { + /* fraction of seconds */ + if (*p == '.') ++p; + st.slen = end - p + 1; + } else { + /* other parts always 2 digits length */ + st.slen = 2; + } + st.ptr = p; + + parts[i] = pj_strtoul(&st); + p += st.slen; + } + + /* encode parts to pj_time_val */ + pt.year = parts[0]; + if (utc) + pt.year += (pt.year < 50)? 2000:1900; + pt.mon = parts[1] - 1; + pt.day = parts[2]; + pt.hour = parts[3]; + pt.min = parts[4]; + pt.sec = parts[5]; + pt.msec = parts[6]; + + pj_time_encode(&pt, tv); + + return PJ_TRUE; +} + + +/* Get Common Name field string from a general name string */ +static void get_cn_from_gen_name(const pj_str_t *gen_name, pj_str_t *cn) +{ + pj_str_t CN_sign = {"/CN=", 4}; + char *p, *q; + + pj_bzero(cn, sizeof(cn)); + + p = pj_strstr(gen_name, &CN_sign); + if (!p) + return; + + p += 4; /* shift pointer to value part */ + pj_strset(cn, p, gen_name->slen - (p - gen_name->ptr)); + q = pj_strchr(cn, '/'); + if (q) + cn->slen = q - p; +} + + +/* Get certificate info from OpenSSL X509, in case the certificate info + * hal already populated, this function will check if the contents need + * to be updated by inspecting the issuer and the serial number. + */ +static void get_cert_info(pj_pool_t *pool, pj_ssl_cert_info *ci, X509 *x) +{ + pj_bool_t update_needed; + char buf[512]; + pj_uint8_t serial_no[64] = {0}; /* should be >= sizeof(ci->serial_no) */ + pj_uint8_t *p; + unsigned len; + GENERAL_NAMES *names = NULL; + + pj_assert(pool && ci && x); + + /* Get issuer */ + X509_NAME_oneline(X509_get_issuer_name(x), buf, sizeof(buf)); + + /* Get serial no */ + p = (pj_uint8_t*) M_ASN1_STRING_data(X509_get_serialNumber(x)); + len = M_ASN1_STRING_length(X509_get_serialNumber(x)); + if (len > sizeof(ci->serial_no)) + len = sizeof(ci->serial_no); + pj_memcpy(serial_no + sizeof(ci->serial_no) - len, p, len); + + /* Check if the contents need to be updated. */ + update_needed = pj_strcmp2(&ci->issuer.info, buf) || + pj_memcmp(ci->serial_no, serial_no, sizeof(ci->serial_no)); + if (!update_needed) + return; + + /* Update cert info */ + + pj_bzero(ci, sizeof(pj_ssl_cert_info)); + + /* Version */ + ci->version = X509_get_version(x) + 1; + + /* Issuer */ + pj_strdup2(pool, &ci->issuer.info, buf); + get_cn_from_gen_name(&ci->issuer.info, &ci->issuer.cn); + + /* Serial number */ + pj_memcpy(ci->serial_no, serial_no, sizeof(ci->serial_no)); + + /* Subject */ + pj_strdup2(pool, &ci->subject.info, + X509_NAME_oneline(X509_get_subject_name(x), + buf, sizeof(buf))); + get_cn_from_gen_name(&ci->subject.info, &ci->subject.cn); + + /* Validity */ + parse_ossl_asn1_time(&ci->validity.start, &ci->validity.gmt, + X509_get_notBefore(x)); + parse_ossl_asn1_time(&ci->validity.end, &ci->validity.gmt, + X509_get_notAfter(x)); + + /* Subject Alternative Name extension */ + if (ci->version >= 3) { + names = (GENERAL_NAMES*) X509_get_ext_d2i(x, NID_subject_alt_name, + NULL, NULL); + } + if (names) { + unsigned i, cnt; + + cnt = sk_GENERAL_NAME_num(names); + ci->subj_alt_name.entry = pj_pool_calloc(pool, cnt, + sizeof(*ci->subj_alt_name.entry)); + + for (i = 0; i < cnt; ++i) { + unsigned char *p = 0; + pj_ssl_cert_name_type type = PJ_SSL_CERT_NAME_UNKNOWN; + const GENERAL_NAME *name; + + name = sk_GENERAL_NAME_value(names, i); + + switch (name->type) { + case GEN_EMAIL: + len = ASN1_STRING_to_UTF8(&p, name->d.ia5); + type = PJ_SSL_CERT_NAME_RFC822; + break; + case GEN_DNS: + len = ASN1_STRING_to_UTF8(&p, name->d.ia5); + type = PJ_SSL_CERT_NAME_DNS; + break; + case GEN_URI: + len = ASN1_STRING_to_UTF8(&p, name->d.ia5); + type = PJ_SSL_CERT_NAME_URI; + break; + case GEN_IPADD: + p = ASN1_STRING_data(name->d.ip); + len = ASN1_STRING_length(name->d.ip); + type = PJ_SSL_CERT_NAME_IP; + break; + default: + break; + } + + if (p && len && type != PJ_SSL_CERT_NAME_UNKNOWN) { + ci->subj_alt_name.entry[ci->subj_alt_name.cnt].type = type; + if (type == PJ_SSL_CERT_NAME_IP) { + int af = pj_AF_INET(); + if (len == sizeof(pj_in6_addr)) af = pj_AF_INET6(); + pj_inet_ntop2(af, p, buf, sizeof(buf)); + pj_strdup2(pool, + &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name, + buf); + } else { + pj_strdup2(pool, + &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name, + (char*)p); + OPENSSL_free(p); + } + ci->subj_alt_name.cnt++; + } + } + } +} + + +/* Update local & remote certificates info. This function should be + * called after handshake or renegotiation successfully completed. + */ +static void update_certs_info(pj_ssl_sock_t *ssock) +{ + X509 *x; + + pj_assert(ssock->ssl_state == SSL_STATE_ESTABLISHED); + + /* Active local certificate */ + x = SSL_get_certificate(ssock->ossl_ssl); + if (x) { + get_cert_info(ssock->pool, &ssock->local_cert_info, x); + /* Don't free local's X509! */ + } else { + pj_bzero(&ssock->local_cert_info, sizeof(pj_ssl_cert_info)); + } + + /* Active remote certificate */ + x = SSL_get_peer_certificate(ssock->ossl_ssl); + if (x) { + get_cert_info(ssock->pool, &ssock->remote_cert_info, x); + /* Free peer's X509 */ + X509_free(x); + } else { + pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info)); + } +} + + +/* When handshake completed: + * - notify application + * - if handshake failed, reset SSL state + * - return PJ_FALSE when SSL socket instance is destroyed by application. + */ +static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock, + pj_status_t status) +{ + /* Cancel handshake timer */ + if (ssock->timer.id == TIMER_HANDSHAKE_TIMEOUT) { + pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer); + ssock->timer.id = TIMER_NONE; + } + + /* Update certificates info on successful handshake */ + if (status == PJ_SUCCESS) + update_certs_info(ssock); + + /* Accepting */ + if (ssock->is_server) { + if (status != PJ_SUCCESS) { + /* Handshake failed in accepting, destroy our self silently. */ + + char errmsg[PJ_ERR_MSG_SIZE]; + char buf[PJ_INET6_ADDRSTRLEN+10]; + + pj_strerror(status, errmsg, sizeof(errmsg)); + PJ_LOG(3,(ssock->pool->obj_name, "Handshake failed in accepting " + "%s: %s", + pj_sockaddr_print(&ssock->rem_addr, buf, sizeof(buf), 3), + errmsg)); + + /* Workaround for ticket #985 */ +#if defined(PJ_WIN32) && PJ_WIN32!=0 + if (ssock->param.timer_heap) { + pj_time_val interval = {0, DELAYED_CLOSE_TIMEOUT}; + + reset_ssl_sock_state(ssock); + + ssock->timer.id = TIMER_CLOSE; + pj_time_val_normalize(&interval); + if (pj_timer_heap_schedule(ssock->param.timer_heap, + &ssock->timer, &interval) != 0) + { + ssock->timer.id = TIMER_NONE; + pj_ssl_sock_close(ssock); + } + } else +#endif /* PJ_WIN32 */ + { + pj_ssl_sock_close(ssock); + } + return PJ_FALSE; + } + /* Notify application the newly accepted SSL socket */ + if (ssock->param.cb.on_accept_complete) { + pj_bool_t ret; + ret = (*ssock->param.cb.on_accept_complete) + (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr, + pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr)); + if (ret == PJ_FALSE) + return PJ_FALSE; + } + } + + /* Connecting */ + else { + /* On failure, reset SSL socket state first, as app may try to + * reconnect in the callback. + */ + if (status != PJ_SUCCESS) { + /* Server disconnected us, possibly due to SSL nego failure */ + if (status == PJ_EEOF) { + unsigned long err; + err = ERR_get_error(); + if (err != SSL_ERROR_NONE) + status = STATUS_FROM_SSL_ERR(ssock, err); + } + reset_ssl_sock_state(ssock); + } + if (ssock->param.cb.on_connect_complete) { + pj_bool_t ret; + ret = (*ssock->param.cb.on_connect_complete)(ssock, status); + if (ret == PJ_FALSE) + return PJ_FALSE; + } + } + + return PJ_TRUE; +} + +/* Flush write BIO to network socket. Note that any access to write BIO + * MUST be serialized, so mutex protection must cover any call to OpenSSL + * API (that possibly generate data for write BIO) along with the call to + * this function (flushing all data in write BIO generated by above + * OpenSSL API call). + */ +static pj_status_t flush_write_bio(pj_ssl_sock_t *ssock, + pj_ioqueue_op_key_t *send_key, + pj_size_t orig_len, + unsigned flags) +{ + char *data; + pj_ssize_t len; + + write_state_t *write_st = &ssock->write_state; + write_data_t *wdata; + pj_size_t avail_len, needed_len, skipped_len = 0; + pj_status_t status; + + /* Check if there is data in write BIO, flush it if any */ + if (!BIO_pending(ssock->ossl_wbio)) + return PJ_SUCCESS; + + /* Get data and its length */ + len = BIO_get_mem_data(ssock->ossl_wbio, &data); + if (len == 0) + return PJ_SUCCESS; + + /* Calculate buffer size needed, and align it to 8 */ + needed_len = len + sizeof(write_data_t); + needed_len = ((needed_len + 7) >> 3) << 3; + + /* Check buffer availability */ + avail_len = write_st->max_len - write_st->len; + if (avail_len < needed_len) + return PJ_ENOMEM; + + /* More buffer availability check, note that the write data must be in + * a contigue buffer. + */ + if (write_st->len == 0) { + + write_st->start = write_st->buf; + wdata = (write_data_t*)write_st->start; + + } else { + + char *reg1, *reg2; + pj_size_t reg1_len, reg2_len; + + /* Unused slots may be wrapped/splitted into two regions, so let's + * analyze them if any region can hold the write data. + */ + reg1 = write_st->start + write_st->len; + if (reg1 >= write_st->buf + write_st->max_len) + reg1 -= write_st->max_len; + reg1_len = write_st->max_len - write_st->len; + if (reg1 + reg1_len > write_st->buf + write_st->max_len) { + reg1_len = write_st->buf + write_st->max_len - reg1; + reg2 = write_st->buf; + reg2_len = write_st->start - write_st->buf; + } else { + reg2 = NULL; + reg2_len = 0; + } + avail_len = PJ_MAX(reg1_len, reg2_len); + if (avail_len < needed_len) + return PJ_ENOMEM; + + /* Get write data pointer and update buffer length */ + if (reg1_len >= needed_len) { + wdata = (write_data_t*)reg1; + } else { + wdata = (write_data_t*)reg2; + /* Unused slot in region 1 is skipped as current write data + * doesn't fit it. + */ + skipped_len = reg1_len; + } + } + + /* Copy the data and set its properties into the buffer */ + pj_bzero(wdata, sizeof(write_data_t)); + wdata->app_key = send_key; + wdata->record_len = needed_len; + wdata->data_len = len; + wdata->plain_data_len = orig_len; + wdata->flags = flags; + pj_memcpy(&wdata->data, data, len); + + /* Send it */ + if (ssock->param.sock_type == pj_SOCK_STREAM()) { + status = pj_activesock_send(ssock->asock, &wdata->key, + wdata->data.content, &len, + flags); + } else { + status = pj_activesock_sendto(ssock->asock, &wdata->key, + wdata->data.content, &len, + flags, + (pj_sockaddr_t*)&ssock->rem_addr, + ssock->addr_len); + } + + /* Oh no, EWOULDBLOCK! */ + if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { + /* Just return PJ_SUCCESS here, the pending data will be sent in next + * call of this function since the data is still stored in write BIO. + */ + return PJ_SUCCESS; + } + + /* Reset write BIO after flushed */ + BIO_reset(ssock->ossl_wbio); + + if (status == PJ_EPENDING) { + /* Update write state */ + pj_assert(skipped_len==0 || write_st->last_data); + write_st->len += needed_len + skipped_len; + if (write_st->last_data) + write_st->last_data->record_len += skipped_len; + write_st->last_data = wdata; + } + + return status; +} + + +static void on_timer(pj_timer_heap_t *th, struct pj_timer_entry *te) +{ + pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)te->user_data; + int timer_id = te->id; + + te->id = TIMER_NONE; + + PJ_UNUSED_ARG(th); + + switch (timer_id) { + case TIMER_HANDSHAKE_TIMEOUT: + PJ_LOG(1,(ssock->pool->obj_name, "SSL timeout after %d.%ds", + ssock->param.timeout.sec, ssock->param.timeout.msec)); + + on_handshake_complete(ssock, PJ_ETIMEDOUT); + break; + case TIMER_CLOSE: + pj_ssl_sock_close(ssock); + break; + default: + pj_assert(!"Unknown timer"); + break; + } +} + + +/* Asynchronouse handshake */ +static pj_status_t do_handshake(pj_ssl_sock_t *ssock) +{ + pj_status_t status; + int err; + + pj_lock_acquire(ssock->write_mutex); + + /* Perform SSL handshake */ + err = SSL_do_handshake(ssock->ossl_ssl); + + /* SSL_do_handshake() may put some pending data into SSL write BIO, + * flush it if any. + */ + status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + pj_lock_release(ssock->write_mutex); + return status; + } + + pj_lock_release(ssock->write_mutex); + + if (err < 0) { + err = SSL_get_error(ssock->ossl_ssl, err); + if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) + { + /* Handshake fails */ + status = STATUS_FROM_SSL_ERR(ssock, err); + return status; + } + } + + /* Check if handshake has been completed */ + if (SSL_is_init_finished(ssock->ossl_ssl)) { + ssock->ssl_state = SSL_STATE_ESTABLISHED; + return PJ_SUCCESS; + } + + return PJ_EPENDING; +} + + +/* + ******************************************************************* + * Active socket callbacks. + ******************************************************************* + */ + +static pj_bool_t asock_on_data_read (pj_activesock_t *asock, + void *data, + pj_size_t size, + pj_status_t status, + pj_size_t *remainder) +{ + pj_ssl_sock_t *ssock = (pj_ssl_sock_t*) + pj_activesock_get_user_data(asock); + pj_size_t nwritten; + + /* Socket error or closed */ + if (data && size > 0) { + /* Consume the whole data */ + nwritten = BIO_write(ssock->ossl_rbio, data, size); + if (nwritten < size) { + status = GET_SSL_STATUS(ssock); + goto on_error; + } + } + + /* Check if SSL handshake hasn't finished yet */ + if (ssock->ssl_state == SSL_STATE_HANDSHAKING) { + pj_bool_t ret = PJ_TRUE; + + if (status == PJ_SUCCESS) + status = do_handshake(ssock); + + /* Not pending is either success or failed */ + if (status != PJ_EPENDING) + ret = on_handshake_complete(ssock, status); + + return ret; + } + + /* See if there is any decrypted data for the application */ + if (ssock->read_started) { + do { + read_data_t *buf = *(OFFSET_OF_READ_DATA_PTR(ssock, data)); + void *data_ = (pj_int8_t*)buf->data + buf->len; + int size_ = ssock->read_size - buf->len; + + /* SSL_read() may write some data to BIO write when re-negotiation + * is on progress, so let's protect it with write mutex. + */ + pj_lock_acquire(ssock->write_mutex); + size_ = SSL_read(ssock->ossl_ssl, data_, size_); + pj_lock_release(ssock->write_mutex); + + if (size_ > 0 || status != PJ_SUCCESS) { + if (ssock->param.cb.on_data_read) { + pj_bool_t ret; + pj_size_t remainder_ = 0; + + if (size_ > 0) + buf->len += size_; + + ret = (*ssock->param.cb.on_data_read)(ssock, buf->data, + buf->len, status, + &remainder_); + if (!ret) { + /* We've been destroyed */ + return PJ_FALSE; + } + + /* Application may have left some data to be consumed + * later. + */ + buf->len = remainder_; + } + + /* Active socket signalled connection closed/error, this has + * been signalled to the application along with any remaining + * buffer. So, let's just reset SSL socket now. + */ + if (status != PJ_SUCCESS) { + reset_ssl_sock_state(ssock); + return PJ_FALSE; + } + + } else { + + int err = SSL_get_error(ssock->ossl_ssl, size); + + /* SSL might just return SSL_ERROR_WANT_READ in + * re-negotiation. + */ + if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) + { + /* Reset SSL socket state, then return PJ_FALSE */ + status = STATUS_FROM_SSL_ERR(ssock, err); + reset_ssl_sock_state(ssock); + goto on_error; + } + + status = do_handshake(ssock); + if (status == PJ_SUCCESS) { + /* Renegotiation completed */ + + /* Update certificates */ + update_certs_info(ssock); + + pj_lock_acquire(ssock->write_mutex); + status = flush_delayed_send(ssock); + pj_lock_release(ssock->write_mutex); + + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + PJ_PERROR(1,(ssock->pool->obj_name, status, + "Failed to flush delayed send")); + goto on_error; + } + } else if (status != PJ_EPENDING) { + PJ_PERROR(1,(ssock->pool->obj_name, status, + "Renegotiation failed")); + goto on_error; + } + + break; + } + } while (1); + } + + return PJ_TRUE; + +on_error: + if (ssock->ssl_state == SSL_STATE_HANDSHAKING) + return on_handshake_complete(ssock, status); + + if (ssock->read_started && ssock->param.cb.on_data_read) { + pj_bool_t ret; + ret = (*ssock->param.cb.on_data_read)(ssock, NULL, 0, status, + remainder); + if (!ret) { + /* We've been destroyed */ + return PJ_FALSE; + } + } + + reset_ssl_sock_state(ssock); + return PJ_FALSE; +} + + +static pj_bool_t asock_on_data_sent (pj_activesock_t *asock, + pj_ioqueue_op_key_t *send_key, + pj_ssize_t sent) +{ + pj_ssl_sock_t *ssock = (pj_ssl_sock_t*) + pj_activesock_get_user_data(asock); + + PJ_UNUSED_ARG(send_key); + PJ_UNUSED_ARG(sent); + + if (ssock->ssl_state == SSL_STATE_HANDSHAKING) { + /* Initial handshaking */ + pj_status_t status; + + status = do_handshake(ssock); + /* Not pending is either success or failed */ + if (status != PJ_EPENDING) + return on_handshake_complete(ssock, status); + + } else if (send_key != &ssock->handshake_op_key) { + /* Some data has been sent, notify application */ + write_data_t *wdata = (write_data_t*)send_key; + if (ssock->param.cb.on_data_sent) { + pj_bool_t ret; + ret = (*ssock->param.cb.on_data_sent)(ssock, wdata->app_key, + wdata->plain_data_len); + if (!ret) { + /* We've been destroyed */ + return PJ_FALSE; + } + } + + /* Update write buffer state */ + pj_lock_acquire(ssock->write_mutex); + ssock->write_state.start += wdata->record_len; + ssock->write_state.len -= wdata->record_len; + if (ssock->write_state.last_data == wdata) { + pj_assert(ssock->write_state.len == 0); + ssock->write_state.last_data = NULL; + } + pj_lock_release(ssock->write_mutex); + + } else { + /* SSL re-negotiation is on-progress, just do nothing */ + } + + return PJ_TRUE; +} + + +static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock, + pj_sock_t newsock, + const pj_sockaddr_t *src_addr, + int src_addr_len) +{ + pj_ssl_sock_t *ssock_parent = (pj_ssl_sock_t*) + pj_activesock_get_user_data(asock); + pj_ssl_sock_t *ssock; + pj_activesock_cb asock_cb; + pj_activesock_cfg asock_cfg; + unsigned i; + pj_status_t status; + + PJ_UNUSED_ARG(src_addr_len); + + /* Create new SSL socket instance */ + status = pj_ssl_sock_create(ssock_parent->pool, &ssock_parent->param, + &ssock); + if (status != PJ_SUCCESS) + goto on_return; + + /* Update new SSL socket attributes */ + ssock->sock = newsock; + ssock->parent = ssock_parent; + ssock->is_server = PJ_TRUE; + if (ssock_parent->cert) { + status = pj_ssl_sock_set_certificate(ssock, ssock->pool, + ssock_parent->cert); + if (status != PJ_SUCCESS) + goto on_return; + } + + /* Apply QoS, if specified */ + status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type, + &ssock->param.qos_params, 1, + ssock->pool->obj_name, NULL); + if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error) + goto on_return; + + /* Update local address */ + ssock->addr_len = src_addr_len; + status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, + &ssock->addr_len); + if (status != PJ_SUCCESS) { + /* This fails on few envs, e.g: win IOCP, just tolerate this and + * use parent local address instead. + */ + pj_sockaddr_cp(&ssock->local_addr, &ssock_parent->local_addr); + } + + /* Set remote address */ + pj_sockaddr_cp(&ssock->rem_addr, src_addr); + + /* Create SSL context */ + status = create_ssl(ssock); + if (status != PJ_SUCCESS) + goto on_return; + + /* Prepare read buffer */ + ssock->asock_rbuf = (void**)pj_pool_calloc(ssock->pool, + ssock->param.async_cnt, + sizeof(void*)); + for (i = 0; iparam.async_cnt; ++i) { + ssock->asock_rbuf[i] = (void*) pj_pool_alloc( + ssock->pool, + ssock->param.read_buffer_size + + sizeof(read_data_t*)); + } + + /* Create active socket */ + pj_activesock_cfg_default(&asock_cfg); + asock_cfg.async_cnt = ssock->param.async_cnt; + asock_cfg.concurrency = ssock->param.concurrency; + asock_cfg.whole_data = PJ_TRUE; + + pj_bzero(&asock_cb, sizeof(asock_cb)); + asock_cb.on_data_read = asock_on_data_read; + asock_cb.on_data_sent = asock_on_data_sent; + + status = pj_activesock_create(ssock->pool, + ssock->sock, + ssock->param.sock_type, + &asock_cfg, + ssock->param.ioqueue, + &asock_cb, + ssock, + &ssock->asock); + + if (status != PJ_SUCCESS) + goto on_return; + + /* Start read */ + status = pj_activesock_start_read2(ssock->asock, ssock->pool, + ssock->param.read_buffer_size, + ssock->asock_rbuf, + PJ_IOQUEUE_ALWAYS_ASYNC); + if (status != PJ_SUCCESS) + goto on_return; + + /* Prepare write/send state */ + pj_assert(ssock->write_state.max_len == 0); + ssock->write_state.buf = (char*) + pj_pool_alloc(ssock->pool, + ssock->param.send_buffer_size); + ssock->write_state.max_len = ssock->param.send_buffer_size; + ssock->write_state.start = ssock->write_state.buf; + ssock->write_state.len = 0; + + /* Start handshake timer */ + if (ssock->param.timer_heap && (ssock->param.timeout.sec != 0 || + ssock->param.timeout.msec != 0)) + { + pj_assert(ssock->timer.id == TIMER_NONE); + ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT; + status = pj_timer_heap_schedule(ssock->param.timer_heap, + &ssock->timer, + &ssock->param.timeout); + if (status != PJ_SUCCESS) + ssock->timer.id = TIMER_NONE; + } + + /* Start SSL handshake */ + ssock->ssl_state = SSL_STATE_HANDSHAKING; + SSL_set_accept_state(ssock->ossl_ssl); + status = do_handshake(ssock); + +on_return: + if (ssock && status != PJ_EPENDING) + on_handshake_complete(ssock, status); + + /* Must return PJ_TRUE whatever happened, as active socket must + * continue listening. + */ + return PJ_TRUE; +} + + +static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock, + pj_status_t status) +{ + pj_ssl_sock_t *ssock = (pj_ssl_sock_t*) + pj_activesock_get_user_data(asock); + unsigned i; + + if (status != PJ_SUCCESS) + goto on_return; + + /* Update local address */ + ssock->addr_len = sizeof(pj_sockaddr); + status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, + &ssock->addr_len); + if (status != PJ_SUCCESS) + goto on_return; + + /* Create SSL context */ + status = create_ssl(ssock); + if (status != PJ_SUCCESS) + goto on_return; + + /* Prepare read buffer */ + ssock->asock_rbuf = (void**)pj_pool_calloc(ssock->pool, + ssock->param.async_cnt, + sizeof(void*)); + for (i = 0; iparam.async_cnt; ++i) { + ssock->asock_rbuf[i] = (void*) pj_pool_alloc( + ssock->pool, + ssock->param.read_buffer_size + + sizeof(read_data_t*)); + } + + /* Start read */ + status = pj_activesock_start_read2(ssock->asock, ssock->pool, + ssock->param.read_buffer_size, + ssock->asock_rbuf, + PJ_IOQUEUE_ALWAYS_ASYNC); + if (status != PJ_SUCCESS) + goto on_return; + + /* Prepare write/send state */ + pj_assert(ssock->write_state.max_len == 0); + ssock->write_state.buf = (char*) + pj_pool_alloc(ssock->pool, + ssock->param.send_buffer_size); + ssock->write_state.max_len = ssock->param.send_buffer_size; + ssock->write_state.start = ssock->write_state.buf; + ssock->write_state.len = 0; + +#ifdef SSL_set_tlsext_host_name + /* Set server name to connect */ + if (ssock->param.server_name.slen) { + /* Server name is null terminated already */ + if (!SSL_set_tlsext_host_name(ssock->ossl_ssl, + ssock->param.server_name.ptr)) + { + char err_str[PJ_ERR_MSG_SIZE]; + + ERR_error_string_n(ERR_get_error(), err_str, sizeof(err_str)); + PJ_LOG(3,(ssock->pool->obj_name, "SSL_set_tlsext_host_name() " + "failed: %s", err_str)); + } + } +#endif + + /* Start SSL handshake */ + ssock->ssl_state = SSL_STATE_HANDSHAKING; + SSL_set_connect_state(ssock->ossl_ssl); + + status = do_handshake(ssock); + if (status != PJ_EPENDING) + goto on_return; + + return PJ_TRUE; + +on_return: + return on_handshake_complete(ssock, status); +} + + + +/* + ******************************************************************* + * API + ******************************************************************* + */ + +/* Load credentials from files. */ +PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files (pj_pool_t *pool, + const pj_str_t *CA_file, + const pj_str_t *cert_file, + const pj_str_t *privkey_file, + const pj_str_t *privkey_pass, + pj_ssl_cert_t **p_cert) +{ + pj_ssl_cert_t *cert; + + PJ_ASSERT_RETURN(pool && CA_file && cert_file && privkey_file, PJ_EINVAL); + + cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t); + pj_strdup_with_null(pool, &cert->CA_file, CA_file); + pj_strdup_with_null(pool, &cert->cert_file, cert_file); + pj_strdup_with_null(pool, &cert->privkey_file, privkey_file); + pj_strdup_with_null(pool, &cert->privkey_pass, privkey_pass); + + *p_cert = cert; + + return PJ_SUCCESS; +} + + +/* Set SSL socket credentials. */ +PJ_DECL(pj_status_t) pj_ssl_sock_set_certificate( + pj_ssl_sock_t *ssock, + pj_pool_t *pool, + const pj_ssl_cert_t *cert) +{ + pj_ssl_cert_t *cert_; + + PJ_ASSERT_RETURN(ssock && pool && cert, PJ_EINVAL); + + cert_ = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t); + pj_memcpy(cert_, cert, sizeof(cert)); + pj_strdup_with_null(pool, &cert_->CA_file, &cert->CA_file); + pj_strdup_with_null(pool, &cert_->cert_file, &cert->cert_file); + pj_strdup_with_null(pool, &cert_->privkey_file, &cert->privkey_file); + pj_strdup_with_null(pool, &cert_->privkey_pass, &cert->privkey_pass); + + ssock->cert = cert_; + + return PJ_SUCCESS; +} + + +/* Get available ciphers. */ +PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[], + unsigned *cipher_num) +{ + unsigned i; + + PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL); + + if (openssl_cipher_num == 0) { + init_openssl(); + shutdown_openssl(); + } + + if (openssl_cipher_num == 0) { + *cipher_num = 0; + return PJ_ENOTFOUND; + } + + *cipher_num = PJ_MIN(*cipher_num, openssl_cipher_num); + + for (i = 0; i < *cipher_num; ++i) + ciphers[i] = openssl_ciphers[i].id; + + return PJ_SUCCESS; +} + + +/* Get cipher name string */ +PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher) +{ + unsigned i; + + if (openssl_cipher_num == 0) { + init_openssl(); + shutdown_openssl(); + } + + for (i = 0; i < openssl_cipher_num; ++i) { + if (cipher == openssl_ciphers[i].id) + return openssl_ciphers[i].name; + } + + return NULL; +} + +/* Check if the specified cipher is supported by SSL/TLS backend. */ +PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher) +{ + unsigned i; + + if (openssl_cipher_num == 0) { + init_openssl(); + shutdown_openssl(); + } + + for (i = 0; i < openssl_cipher_num; ++i) { + if (cipher == openssl_ciphers[i].id) + return PJ_TRUE; + } + + return PJ_FALSE; +} + + +/* + * Create SSL socket instance. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool, + const pj_ssl_sock_param *param, + pj_ssl_sock_t **p_ssock) +{ + pj_ssl_sock_t *ssock; + pj_status_t status; + + PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL); + PJ_ASSERT_RETURN(param->sock_type == pj_SOCK_STREAM(), PJ_ENOTSUP); + + pool = pj_pool_create(pool->factory, "ssl%p", 512, 512, NULL); + + /* Create secure socket */ + ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t); + ssock->pool = pool; + ssock->sock = PJ_INVALID_SOCKET; + ssock->ssl_state = SSL_STATE_NULL; + pj_list_init(&ssock->write_pending); + pj_list_init(&ssock->write_pending_empty); + pj_timer_entry_init(&ssock->timer, 0, ssock, &on_timer); + + /* Create secure socket mutex */ + status = pj_lock_create_recursive_mutex(pool, pool->obj_name, + &ssock->write_mutex); + if (status != PJ_SUCCESS) + return status; + + /* Init secure socket param */ + ssock->param = *param; + ssock->param.read_buffer_size = ((ssock->param.read_buffer_size+7)>>3)<<3; + if (param->ciphers_num > 0) { + unsigned i; + ssock->param.ciphers = (pj_ssl_cipher*) + pj_pool_calloc(pool, param->ciphers_num, + sizeof(pj_ssl_cipher)); + for (i = 0; i < param->ciphers_num; ++i) + ssock->param.ciphers[i] = param->ciphers[i]; + } + + /* Server name must be null-terminated */ + pj_strdup_with_null(pool, &ssock->param.server_name, + ¶m->server_name); + + /* Finally */ + *p_ssock = ssock; + + return PJ_SUCCESS; +} + + +/* + * Close the secure socket. This will unregister the socket from the + * ioqueue and ultimately close the socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock) +{ + pj_pool_t *pool; + + PJ_ASSERT_RETURN(ssock, PJ_EINVAL); + + if (!ssock->pool) + return PJ_SUCCESS; + + if (ssock->timer.id != TIMER_NONE) { + pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer); + ssock->timer.id = TIMER_NONE; + } + + reset_ssl_sock_state(ssock); + pj_lock_destroy(ssock->write_mutex); + + pool = ssock->pool; + ssock->pool = NULL; + if (pool) + pj_pool_release(pool); + + return PJ_SUCCESS; +} + + +/* + * Associate arbitrary data with the secure socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data(pj_ssl_sock_t *ssock, + void *user_data) +{ + PJ_ASSERT_RETURN(ssock, PJ_EINVAL); + + ssock->param.user_data = user_data; + return PJ_SUCCESS; +} + + +/* + * Retrieve the user data previously associated with this secure + * socket. + */ +PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock) +{ + PJ_ASSERT_RETURN(ssock, NULL); + + return ssock->param.user_data; +} + + +/* + * Retrieve the local address and port used by specified SSL socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock, + pj_ssl_sock_info *info) +{ + pj_bzero(info, sizeof(*info)); + + /* Established flag */ + info->established = (ssock->ssl_state == SSL_STATE_ESTABLISHED); + + /* Protocol */ + info->proto = ssock->param.proto; + + /* Local address */ + pj_sockaddr_cp(&info->local_addr, &ssock->local_addr); + + if (info->established) { + const SSL_CIPHER *cipher; + + /* Current cipher */ + cipher = SSL_get_current_cipher(ssock->ossl_ssl); + info->cipher = (cipher->id & 0x00FFFFFF); + + /* Remote address */ + pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr); + + /* Certificates info */ + info->local_cert_info = &ssock->local_cert_info; + info->remote_cert_info = &ssock->remote_cert_info; + + /* Verification status */ + info->verify_status = ssock->verify_status; + } + + /* Last known OpenSSL error code */ + info->last_native_err = ssock->last_err; + + return PJ_SUCCESS; +} + + +/* + * Starts read operation on this secure socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_read (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + unsigned buff_size, + pj_uint32_t flags) +{ + void **readbuf; + unsigned i; + + PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL); + PJ_ASSERT_RETURN(ssock->ssl_state==SSL_STATE_ESTABLISHED, PJ_EINVALIDOP); + + readbuf = (void**) pj_pool_calloc(pool, ssock->param.async_cnt, + sizeof(void*)); + + for (i=0; iparam.async_cnt; ++i) { + readbuf[i] = pj_pool_alloc(pool, buff_size); + } + + return pj_ssl_sock_start_read2(ssock, pool, buff_size, + readbuf, flags); +} + + +/* + * Same as #pj_ssl_sock_start_read(), except that the application + * supplies the buffers for the read operation so that the acive socket + * does not have to allocate the buffers. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + unsigned buff_size, + void *readbuf[], + pj_uint32_t flags) +{ + unsigned i; + + PJ_ASSERT_RETURN(ssock && pool && buff_size && readbuf, PJ_EINVAL); + PJ_ASSERT_RETURN(ssock->ssl_state==SSL_STATE_ESTABLISHED, PJ_EINVALIDOP); + + /* Create SSL socket read buffer */ + ssock->ssock_rbuf = (read_data_t*)pj_pool_calloc(pool, + ssock->param.async_cnt, + sizeof(read_data_t)); + + /* Store SSL socket read buffer pointer in the activesock read buffer */ + for (i=0; iparam.async_cnt; ++i) { + read_data_t **p_ssock_rbuf = + OFFSET_OF_READ_DATA_PTR(ssock, ssock->asock_rbuf[i]); + + ssock->ssock_rbuf[i].data = readbuf[i]; + ssock->ssock_rbuf[i].len = 0; + + *p_ssock_rbuf = &ssock->ssock_rbuf[i]; + } + + ssock->read_size = buff_size; + ssock->read_started = PJ_TRUE; + ssock->read_flags = flags; + + return PJ_SUCCESS; +} + + +/* + * Same as pj_ssl_sock_start_read(), except that this function is used + * only for datagram sockets, and it will trigger \a on_data_recvfrom() + * callback instead. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + unsigned buff_size, + pj_uint32_t flags) +{ + PJ_UNUSED_ARG(ssock); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(buff_size); + PJ_UNUSED_ARG(flags); + + return PJ_ENOTSUP; +} + + +/* + * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() + * operation takes the buffer from the argument rather than creating + * new ones. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + unsigned buff_size, + void *readbuf[], + pj_uint32_t flags) +{ + PJ_UNUSED_ARG(ssock); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(buff_size); + PJ_UNUSED_ARG(readbuf); + PJ_UNUSED_ARG(flags); + + return PJ_ENOTSUP; +} + +/* Write plain data to SSL and flush write BIO. Note that accessing + * write BIO must be serialized, so a call to this function must be + * protected by write mutex of SSL socket. + */ +static pj_status_t ssl_write(pj_ssl_sock_t *ssock, + pj_ioqueue_op_key_t *send_key, + const void *data, + pj_ssize_t size, + unsigned flags) +{ + pj_status_t status; + int nwritten; + + /* Write the plain data to SSL, after SSL encrypts it, write BIO will + * contain the secured data to be sent via socket. Note that re- + * negotitation may be on progress, so sending data should be delayed + * until re-negotiation is completed. + */ + nwritten = SSL_write(ssock->ossl_ssl, data, size); + + if (nwritten == size) { + /* All data written, flush write BIO to network socket */ + status = flush_write_bio(ssock, send_key, size, flags); + } else if (nwritten <= 0) { + /* SSL failed to process the data, it may just that re-negotiation + * is on progress. + */ + int err; + err = SSL_get_error(ssock->ossl_ssl, nwritten); + if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_NONE) { + /* Re-negotiation is on progress, flush re-negotiation data */ + status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0); + if (status == PJ_SUCCESS || status == PJ_EPENDING) + /* Just return PJ_EBUSY when re-negotiation is on progress */ + status = PJ_EBUSY; + } else { + /* Some problem occured */ + status = STATUS_FROM_SSL_ERR(ssock, err); + } + } else { + /* nwritten < *size, shouldn't happen, unless write BIO cannot hold + * the whole secured data, perhaps because of insufficient memory. + */ + status = PJ_ENOMEM; + } + + return status; +} + +/* Flush delayed data sending in the write pending list. Note that accessing + * write pending list must be serialized, so a call to this function must be + * protected by write mutex of SSL socket. + */ +static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock) +{ + while (!pj_list_empty(&ssock->write_pending)) { + write_pending_t *wp; + pj_status_t status; + + wp = ssock->write_pending.next; + + status = ssl_write(ssock, &wp->data.key, wp->data.data.ptr, + wp->data.plain_data_len, wp->data.flags); + if (status != PJ_SUCCESS) + return status; + + pj_list_erase(wp); + pj_list_push_back(&ssock->write_pending_empty, wp); + } + + return PJ_SUCCESS; +} + +/* Sending is delayed, push back the sending data into pending list. Note that + * accessing write pending list must be serialized, so a call to this function + * must be protected by write mutex of SSL socket. + */ +static pj_status_t delay_send (pj_ssl_sock_t *ssock, + pj_ioqueue_op_key_t *send_key, + const void *data, + pj_ssize_t size, + unsigned flags) +{ + write_pending_t *wp; + + /* Init write pending instance */ + if (!pj_list_empty(&ssock->write_pending_empty)) { + wp = ssock->write_pending_empty.next; + pj_list_erase(wp); + } else { + wp = PJ_POOL_ZALLOC_T(ssock->pool, write_pending_t); + } + + wp->data.app_key = send_key; + wp->data.plain_data_len = size; + wp->data.data.ptr = data; + wp->data.flags = flags; + + pj_list_push_back(&ssock->write_pending, wp); + + /* Must return PJ_EPENDING */ + return PJ_EPENDING; +} + +/** + * Send data using the socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock, + pj_ioqueue_op_key_t *send_key, + const void *data, + pj_ssize_t *size, + unsigned flags) +{ + pj_status_t status; + + PJ_ASSERT_RETURN(ssock && data && size && (*size>0), PJ_EINVAL); + PJ_ASSERT_RETURN(ssock->ssl_state==SSL_STATE_ESTABLISHED, PJ_EINVALIDOP); + + pj_lock_acquire(ssock->write_mutex); + + /* Flush delayed send first. Sending data might be delayed when + * re-negotiation is on-progress. + */ + status = flush_delayed_send(ssock); + if (status == PJ_EBUSY) { + /* Re-negotiation is on progress, delay sending */ + status = delay_send(ssock, send_key, data, *size, flags); + goto on_return; + } else if (status != PJ_SUCCESS) { + goto on_return; + } + + /* Write data to SSL */ + status = ssl_write(ssock, send_key, data, *size, flags); + if (status == PJ_EBUSY) { + /* Re-negotiation is on progress, delay sending */ + status = delay_send(ssock, send_key, data, *size, flags); + } + +on_return: + pj_lock_release(ssock->write_mutex); + return status; +} + + +/** + * Send datagram using the socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock, + pj_ioqueue_op_key_t *send_key, + const void *data, + pj_ssize_t *size, + unsigned flags, + const pj_sockaddr_t *addr, + int addr_len) +{ + PJ_UNUSED_ARG(ssock); + PJ_UNUSED_ARG(send_key); + PJ_UNUSED_ARG(data); + PJ_UNUSED_ARG(size); + PJ_UNUSED_ARG(flags); + PJ_UNUSED_ARG(addr); + PJ_UNUSED_ARG(addr_len); + + return PJ_ENOTSUP; +} + + +/** + * Starts asynchronous socket accept() operations on this secure socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + const pj_sockaddr_t *localaddr, + int addr_len) +{ + pj_activesock_cb asock_cb; + pj_activesock_cfg asock_cfg; + pj_status_t status; + + PJ_ASSERT_RETURN(ssock && pool && localaddr && addr_len, PJ_EINVAL); + + /* Create socket */ + status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, + &ssock->sock); + if (status != PJ_SUCCESS) + goto on_error; + + /* Apply QoS, if specified */ + status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type, + &ssock->param.qos_params, 2, + ssock->pool->obj_name, NULL); + if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error) + goto on_error; + + /* Bind socket */ + status = pj_sock_bind(ssock->sock, localaddr, addr_len); + if (status != PJ_SUCCESS) + goto on_error; + + /* Start listening to the address */ + status = pj_sock_listen(ssock->sock, PJ_SOMAXCONN); + if (status != PJ_SUCCESS) + goto on_error; + + /* Create active socket */ + pj_activesock_cfg_default(&asock_cfg); + asock_cfg.async_cnt = ssock->param.async_cnt; + asock_cfg.concurrency = ssock->param.concurrency; + asock_cfg.whole_data = PJ_TRUE; + + pj_bzero(&asock_cb, sizeof(asock_cb)); + asock_cb.on_accept_complete = asock_on_accept_complete; + + status = pj_activesock_create(pool, + ssock->sock, + ssock->param.sock_type, + &asock_cfg, + ssock->param.ioqueue, + &asock_cb, + ssock, + &ssock->asock); + + if (status != PJ_SUCCESS) + goto on_error; + + /* Start accepting */ + status = pj_activesock_start_accept(ssock->asock, pool); + if (status != PJ_SUCCESS) + goto on_error; + + /* Update local address */ + ssock->addr_len = addr_len; + status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, + &ssock->addr_len); + if (status != PJ_SUCCESS) + pj_sockaddr_cp(&ssock->local_addr, localaddr); + + ssock->is_server = PJ_TRUE; + + return PJ_SUCCESS; + +on_error: + reset_ssl_sock_state(ssock); + return status; +} + + +/** + * Starts asynchronous socket connect() operation. + */ +PJ_DECL(pj_status_t) pj_ssl_sock_start_connect(pj_ssl_sock_t *ssock, + pj_pool_t *pool, + const pj_sockaddr_t *localaddr, + const pj_sockaddr_t *remaddr, + int addr_len) +{ + pj_activesock_cb asock_cb; + pj_activesock_cfg asock_cfg; + pj_status_t status; + + PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len, + PJ_EINVAL); + + /* Create socket */ + status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, + &ssock->sock); + if (status != PJ_SUCCESS) + goto on_error; + + /* Apply QoS, if specified */ + status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type, + &ssock->param.qos_params, 2, + ssock->pool->obj_name, NULL); + if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error) + goto on_error; + + /* Bind socket */ + status = pj_sock_bind(ssock->sock, localaddr, addr_len); + if (status != PJ_SUCCESS) + goto on_error; + + /* Create active socket */ + pj_activesock_cfg_default(&asock_cfg); + asock_cfg.async_cnt = ssock->param.async_cnt; + asock_cfg.concurrency = ssock->param.concurrency; + asock_cfg.whole_data = PJ_TRUE; + + pj_bzero(&asock_cb, sizeof(asock_cb)); + asock_cb.on_connect_complete = asock_on_connect_complete; + asock_cb.on_data_read = asock_on_data_read; + asock_cb.on_data_sent = asock_on_data_sent; + + status = pj_activesock_create(pool, + ssock->sock, + ssock->param.sock_type, + &asock_cfg, + ssock->param.ioqueue, + &asock_cb, + ssock, + &ssock->asock); + + if (status != PJ_SUCCESS) + goto on_error; + + /* Save remote address */ + pj_sockaddr_cp(&ssock->rem_addr, remaddr); + + /* Start timer */ + if (ssock->param.timer_heap && (ssock->param.timeout.sec != 0 || + ssock->param.timeout.msec != 0)) + { + pj_assert(ssock->timer.id == TIMER_NONE); + ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT; + status = pj_timer_heap_schedule(ssock->param.timer_heap, + &ssock->timer, + &ssock->param.timeout); + if (status != PJ_SUCCESS) + ssock->timer.id = TIMER_NONE; + } + + status = pj_activesock_start_connect(ssock->asock, pool, remaddr, + addr_len); + + if (status == PJ_SUCCESS) + asock_on_connect_complete(ssock->asock, PJ_SUCCESS); + else if (status != PJ_EPENDING) + goto on_error; + + /* Update local address */ + ssock->addr_len = addr_len; + status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, + &ssock->addr_len); + /* Note that we may not get an IP address here. This can + * happen for example on Windows, where getsockname() + * would return 0.0.0.0 if socket has just started the + * async connect. In this case, just leave the local + * address with 0.0.0.0 for now; it will be updated + * once the socket is established. + */ + + /* Update SSL state */ + ssock->is_server = PJ_FALSE; + + return PJ_EPENDING; + +on_error: + reset_ssl_sock_state(ssock); + return status; +} + + +PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock) +{ + int ret; + pj_status_t status; + + PJ_ASSERT_RETURN(ssock->ssl_state == SSL_STATE_ESTABLISHED, PJ_EINVALIDOP); + + if (SSL_renegotiate_pending(ssock->ossl_ssl)) + return PJ_EPENDING; + + ret = SSL_renegotiate(ssock->ossl_ssl); + if (ret <= 0) { + status = GET_SSL_STATUS(ssock); + } else { + status = do_handshake(ssock); + } + + return status; +} + +#endif /* PJ_HAS_SSL_SOCK */ + diff --git a/pjlib/src/pj/ssl_sock_symbian.cpp b/pjlib/src/pj/ssl_sock_symbian.cpp new file mode 100644 index 0000000..6b9b311 --- /dev/null +++ b/pjlib/src/pj/ssl_sock_symbian.cpp @@ -0,0 +1,1426 @@ +/* $Id: ssl_sock_symbian.cpp 3999 2012-03-30 07:10:13Z bennylp $ */ +/* + * Copyright (C) 2009-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 +#include +#include +#include +#include +#include +#include +#include + +#include "os_symbian.h" +#include +#include +#include + +#define THIS_FILE "ssl_sock_symbian.cpp" + + +/* Cipher name structure */ +typedef struct cipher_name_t { + pj_ssl_cipher cipher; + const char *name; +} cipher_name_t; + +/* Cipher name constants */ +static cipher_name_t cipher_names[] = +{ + {PJ_TLS_NULL_WITH_NULL_NULL, "NULL"}, + + /* TLS/SSLv3 */ + {PJ_TLS_RSA_WITH_NULL_MD5, "TLS_RSA_WITH_NULL_MD5"}, + {PJ_TLS_RSA_WITH_NULL_SHA, "TLS_RSA_WITH_NULL_SHA"}, + {PJ_TLS_RSA_WITH_NULL_SHA256, "TLS_RSA_WITH_NULL_SHA256"}, + {PJ_TLS_RSA_WITH_RC4_128_MD5, "TLS_RSA_WITH_RC4_128_MD5"}, + {PJ_TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA"}, + {PJ_TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"}, + {PJ_TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA"}, + {PJ_TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA"}, + {PJ_TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256"}, + {PJ_TLS_RSA_WITH_AES_256_CBC_SHA256, "TLS_RSA_WITH_AES_256_CBC_SHA256"}, + {PJ_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"}, + {PJ_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"}, + {PJ_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"}, + {PJ_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"}, + {PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA, "TLS_DH_DSS_WITH_AES_128_CBC_SHA"}, + {PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA, "TLS_DH_RSA_WITH_AES_128_CBC_SHA"}, + {PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"}, + {PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"}, + {PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA, "TLS_DH_DSS_WITH_AES_256_CBC_SHA"}, + {PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA, "TLS_DH_RSA_WITH_AES_256_CBC_SHA"}, + {PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"}, + {PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"}, + {PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA256, "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"}, + {PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA256, "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"}, + {PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"}, + {PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"}, + {PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA256, "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"}, + {PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA256, "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"}, + {PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"}, + {PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"}, + {PJ_TLS_DH_anon_WITH_RC4_128_MD5, "TLS_DH_anon_WITH_RC4_128_MD5"}, + {PJ_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"}, + {PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA, "TLS_DH_anon_WITH_AES_128_CBC_SHA"}, + {PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA, "TLS_DH_anon_WITH_AES_256_CBC_SHA"}, + {PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA256, "TLS_DH_anon_WITH_AES_128_CBC_SHA256"}, + {PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA256, "TLS_DH_anon_WITH_AES_256_CBC_SHA256"}, + + /* TLS (deprecated) */ + {PJ_TLS_RSA_EXPORT_WITH_RC4_40_MD5, "TLS_RSA_EXPORT_WITH_RC4_40_MD5"}, + {PJ_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"}, + {PJ_TLS_RSA_WITH_IDEA_CBC_SHA, "TLS_RSA_WITH_IDEA_CBC_SHA"}, + {PJ_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"}, + {PJ_TLS_RSA_WITH_DES_CBC_SHA, "TLS_RSA_WITH_DES_CBC_SHA"}, + {PJ_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"}, + {PJ_TLS_DH_DSS_WITH_DES_CBC_SHA, "TLS_DH_DSS_WITH_DES_CBC_SHA"}, + {PJ_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"}, + {PJ_TLS_DH_RSA_WITH_DES_CBC_SHA, "TLS_DH_RSA_WITH_DES_CBC_SHA"}, + {PJ_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"}, + {PJ_TLS_DHE_DSS_WITH_DES_CBC_SHA, "TLS_DHE_DSS_WITH_DES_CBC_SHA"}, + {PJ_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"}, + {PJ_TLS_DHE_RSA_WITH_DES_CBC_SHA, "TLS_DHE_RSA_WITH_DES_CBC_SHA"}, + {PJ_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"}, + {PJ_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"}, + {PJ_TLS_DH_anon_WITH_DES_CBC_SHA, "TLS_DH_anon_WITH_DES_CBC_SHA"}, + + /* SSLv3 */ + {PJ_SSL_FORTEZZA_KEA_WITH_NULL_SHA, "SSL_FORTEZZA_KEA_WITH_NULL_SHA"}, + {PJ_SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA,"SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA"}, + {PJ_SSL_FORTEZZA_KEA_WITH_RC4_128_SHA, "SSL_FORTEZZA_KEA_WITH_RC4_128_SHA"}, + + /* SSLv2 */ + {PJ_SSL_CK_RC4_128_WITH_MD5, "SSL_CK_RC4_128_WITH_MD5"}, + {PJ_SSL_CK_RC4_128_EXPORT40_WITH_MD5, "SSL_CK_RC4_128_EXPORT40_WITH_MD5"}, + {PJ_SSL_CK_RC2_128_CBC_WITH_MD5, "SSL_CK_RC2_128_CBC_WITH_MD5"}, + {PJ_SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5, "SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5"}, + {PJ_SSL_CK_IDEA_128_CBC_WITH_MD5, "SSL_CK_IDEA_128_CBC_WITH_MD5"}, + {PJ_SSL_CK_DES_64_CBC_WITH_MD5, "SSL_CK_DES_64_CBC_WITH_MD5"}, + {PJ_SSL_CK_DES_192_EDE3_CBC_WITH_MD5, "SSL_CK_DES_192_EDE3_CBC_WITH_MD5"} +}; + + +/* Get cipher name string */ +static const char* get_cipher_name(pj_ssl_cipher cipher) +{ + unsigned i, n; + + n = PJ_ARRAY_SIZE(cipher_names); + for (i = 0; i < n; ++i) { + if (cipher == cipher_names[i].cipher) + return cipher_names[i].name; + } + + return "CIPHER_UNKNOWN"; +} + +typedef void (*CPjSSLSocket_cb)(int err, void *key); + +class CPjSSLSocketReader : public CActive +{ +public: + static CPjSSLSocketReader *NewL(CSecureSocket &sock) + { + CPjSSLSocketReader *self = new (ELeave) + CPjSSLSocketReader(sock); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + + ~CPjSSLSocketReader() { + Cancel(); + } + + /* Asynchronous read from the socket. */ + int Read(CPjSSLSocket_cb cb, void *key, TPtr8 &data, TUint flags) + { + PJ_ASSERT_RETURN(!IsActive(), PJ_EBUSY); + + cb_ = cb; + key_ = key; + sock_.RecvOneOrMore(data, iStatus, len_received_); + SetActive(); + + return PJ_EPENDING; + } + +private: + CSecureSocket &sock_; + CPjSSLSocket_cb cb_; + void *key_; + TSockXfrLength len_received_; /* not really useful? */ + + void DoCancel() { + sock_.CancelAll(); + } + + void RunL() { + (*cb_)(iStatus.Int(), key_); + } + + CPjSSLSocketReader(CSecureSocket &sock) : + CActive(0), sock_(sock), cb_(NULL), key_(NULL) + {} + + void ConstructL() { + CActiveScheduler::Add(this); + } +}; + +class CPjSSLSocket : public CActive +{ +public: + enum ssl_state { + SSL_STATE_NULL, + SSL_STATE_CONNECTING, + SSL_STATE_HANDSHAKING, + SSL_STATE_ESTABLISHED + }; + + static CPjSSLSocket *NewL(const TDesC8 &ssl_proto, + pj_qos_type qos_type, + const pj_qos_params &qos_params) + { + CPjSSLSocket *self = new (ELeave) CPjSSLSocket(qos_type, qos_params); + CleanupStack::PushL(self); + self->ConstructL(ssl_proto); + CleanupStack::Pop(self); + return self; + } + + ~CPjSSLSocket() { + Cancel(); + CleanupSubObjects(); + } + + int Connect(CPjSSLSocket_cb cb, void *key, const TInetAddr &local_addr, + const TInetAddr &rem_addr, + const TDesC8 &servername = TPtrC8(NULL,0), + const TDesC8 &ciphers = TPtrC8(NULL,0)); + int Send(CPjSSLSocket_cb cb, void *key, const TDesC8 &aDesc, TUint flags); + int SendSync(const TDesC8 &aDesc, TUint flags); + + CPjSSLSocketReader* GetReader(); + enum ssl_state GetState() const { return state_; } + const TInetAddr* GetLocalAddr() const { return &local_addr_; } + int GetCipher(TDes8 &cipher) const { + if (securesock_) + return securesock_->CurrentCipherSuite(cipher); + return KErrNotFound; + } + const CX509Certificate *GetPeerCert() { + if (securesock_) + return securesock_->ServerCert(); + return NULL; + } + +private: + enum ssl_state state_; + pj_sock_t sock_; + CSecureSocket *securesock_; + bool is_connected_; + + pj_qos_type qos_type_; + pj_qos_params qos_params_; + + CPjSSLSocketReader *reader_; + TBuf<32> ssl_proto_; + TInetAddr rem_addr_; + TPtrC8 servername_; + TPtrC8 ciphers_; + TInetAddr local_addr_; + TSockXfrLength sent_len_; + + CPjSSLSocket_cb cb_; + void *key_; + + void DoCancel(); + void RunL(); + + CPjSSLSocket(pj_qos_type qos_type, const pj_qos_params &qos_params) : + CActive(0), state_(SSL_STATE_NULL), sock_(PJ_INVALID_SOCKET), + securesock_(NULL), is_connected_(false), + qos_type_(qos_type), qos_params_(qos_params), + reader_(NULL), cb_(NULL), key_(NULL) + {} + + void ConstructL(const TDesC8 &ssl_proto) { + ssl_proto_.Copy(ssl_proto); + CActiveScheduler::Add(this); + } + + void CleanupSubObjects() { + delete reader_; + reader_ = NULL; + if (securesock_) { + if (state_ == SSL_STATE_ESTABLISHED) + securesock_->Close(); + delete securesock_; + securesock_ = NULL; + } + if (sock_ != PJ_INVALID_SOCKET) { + pj_sock_close(sock_); + sock_ = PJ_INVALID_SOCKET; + } + } +}; + +int CPjSSLSocket::Connect(CPjSSLSocket_cb cb, void *key, + const TInetAddr &local_addr, + const TInetAddr &rem_addr, + const TDesC8 &servername, + const TDesC8 &ciphers) +{ + pj_status_t status; + + PJ_ASSERT_RETURN(state_ == SSL_STATE_NULL, PJ_EINVALIDOP); + + status = pj_sock_socket(rem_addr.Family(), pj_SOCK_STREAM(), 0, &sock_); + if (status != PJ_SUCCESS) + return status; + + // Apply QoS + status = pj_sock_apply_qos2(sock_, qos_type_, &qos_params_, + 2, THIS_FILE, NULL); + + RSocket &rSock = ((CPjSocket*)sock_)->Socket(); + + local_addr_ = local_addr; + + if (!local_addr_.IsUnspecified()) { + TInt err = rSock.Bind(local_addr_); + if (err != KErrNone) + return PJ_RETURN_OS_ERROR(err); + } + + cb_ = cb; + key_ = key; + rem_addr_ = rem_addr; + + /* Note: the following members only keep the pointer, not the data */ + servername_.Set(servername); + ciphers_.Set(ciphers); + + rSock.Connect(rem_addr_, iStatus); + SetActive(); + state_ = SSL_STATE_CONNECTING; + + rSock.LocalName(local_addr_); + + return PJ_EPENDING; +} + +int CPjSSLSocket::Send(CPjSSLSocket_cb cb, void *key, const TDesC8 &aDesc, + TUint flags) +{ + PJ_UNUSED_ARG(flags); + + PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, PJ_EINVALIDOP); + + if (IsActive()) + return PJ_EBUSY; + + cb_ = cb; + key_ = key; + + securesock_->Send(aDesc, iStatus, sent_len_); + SetActive(); + + return PJ_EPENDING; +} + +int CPjSSLSocket::SendSync(const TDesC8 &aDesc, TUint flags) +{ + PJ_UNUSED_ARG(flags); + + PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, PJ_EINVALIDOP); + + TRequestStatus reqStatus; + securesock_->Send(aDesc, reqStatus, sent_len_); + User::WaitForRequest(reqStatus); + + return PJ_RETURN_OS_ERROR(reqStatus.Int()); +} + +CPjSSLSocketReader* CPjSSLSocket::GetReader() +{ + PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, NULL); + + if (reader_) + return reader_; + + TRAPD(err, reader_ = CPjSSLSocketReader::NewL(*securesock_)); + if (err != KErrNone) + return NULL; + + return reader_; +} + +void CPjSSLSocket::DoCancel() +{ + /* Operation to be cancelled depends on current state */ + switch (state_) { + case SSL_STATE_CONNECTING: + { + RSocket &rSock = ((CPjSocket*)sock_)->Socket(); + + rSock.CancelConnect(); + CleanupSubObjects(); + state_ = SSL_STATE_NULL; + } + break; + case SSL_STATE_HANDSHAKING: + { + securesock_->CancelHandshake(); + CleanupSubObjects(); + state_ = SSL_STATE_NULL; + } + break; + case SSL_STATE_ESTABLISHED: + securesock_->CancelSend(); + break; + default: + break; + } +} + +void CPjSSLSocket::RunL() +{ + switch (state_) { + case SSL_STATE_CONNECTING: + if (iStatus != KErrNone) { + CleanupSubObjects(); + state_ = SSL_STATE_NULL; + /* Dispatch connect failure notification */ + if (cb_) (*cb_)(iStatus.Int(), key_); + } else { + RSocket &rSock = ((CPjSocket*)sock_)->Socket(); + + /* Get local addr */ + rSock.LocalName(local_addr_); + + /* Prepare and start handshake */ + securesock_ = CSecureSocket::NewL(rSock, ssl_proto_); + securesock_->SetDialogMode(EDialogModeAttended); + if (servername_.Length() > 0) + securesock_->SetOpt(KSoSSLDomainName, KSolInetSSL, + servername_); + if (ciphers_.Length() > 0) + securesock_->SetAvailableCipherSuites(ciphers_); + + // FlushSessionCache() seems to also fire signals to all + // completed AOs (something like CActiveScheduler::RunIfReady()) + // which may cause problem, e.g: we've experienced that when + // SSL timeout is set to 1s, the SSL timeout timer fires up + // at this point and securesock_ instance gets deleted here! + // So be careful using this. And we don't think we need it here. + //securesock_->FlushSessionCache(); + + securesock_->StartClientHandshake(iStatus); + SetActive(); + state_ = SSL_STATE_HANDSHAKING; + } + break; + case SSL_STATE_HANDSHAKING: + if (iStatus == KErrNone) { + state_ = SSL_STATE_ESTABLISHED; + } else { + state_ = SSL_STATE_NULL; + CleanupSubObjects(); + } + /* Dispatch connect status notification */ + if (cb_) (*cb_)(iStatus.Int(), key_); + break; + case SSL_STATE_ESTABLISHED: + /* Dispatch data sent notification */ + if (cb_) (*cb_)(iStatus.Int(), key_); + break; + default: + pj_assert(0); + break; + } +} + +typedef void (*CPjTimer_cb)(void *user_data); + +class CPjTimer : public CActive +{ +public: + CPjTimer(const pj_time_val *delay, CPjTimer_cb cb, void *user_data) : + CActive(0), cb_(cb), user_data_(user_data) + { + CActiveScheduler::Add(this); + + rtimer_.CreateLocal(); + pj_int32_t interval = PJ_TIME_VAL_MSEC(*delay) * 1000; + if (interval < 0) { + interval = 0; + } + rtimer_.After(iStatus, interval); + SetActive(); + } + + ~CPjTimer() { Cancel(); } + +private: + RTimer rtimer_; + CPjTimer_cb cb_; + void *user_data_; + + void RunL() { if (cb_) (*cb_)(user_data_); } + void DoCancel() { rtimer_.Cancel(); } +}; + +/* + * Structure of recv/read state. + */ +typedef struct read_state_t { + TPtr8 *read_buf; + TPtr8 *orig_buf; + pj_uint32_t flags; +} read_state_t; + +/* + * Structure of send/write data. + */ +typedef struct write_data_t { + pj_size_t len; + pj_ioqueue_op_key_t *key; + pj_size_t data_len; + char data[1]; +} write_data_t; + +/* + * Structure of send/write state. + */ +typedef struct write_state_t { + char *buf; + pj_size_t max_len; + char *start; + pj_size_t len; + write_data_t *current_data; + TPtrC8 send_ptr; +} write_state_t; + +/* + * Secure socket structure definition. + */ +struct pj_ssl_sock_t +{ + pj_pool_t *pool; + pj_ssl_sock_cb cb; + void *user_data; + + pj_bool_t established; + write_state_t write_state; + read_state_t read_state; + CPjTimer *connect_timer; + + CPjSSLSocket *sock; + int sock_af; + int sock_type; + pj_sockaddr local_addr; + pj_sockaddr rem_addr; + + /* QoS settings */ + pj_qos_type qos_type; + pj_qos_params qos_params; + pj_bool_t qos_ignore_error; + + + pj_ssl_sock_proto proto; + pj_time_val timeout; + pj_str_t servername; + pj_str_t ciphers; + pj_ssl_cert_info remote_cert_info; +}; + + +static pj_str_t get_cert_name(char *buf, unsigned buf_len, + const CX500DistinguishedName &name) +{ + TInt i; + TUint8 *p; + TInt l = buf_len; + + p = (TUint8*)buf; + for(i = 0; i < name.Count(); ++i) { + const CX520AttributeTypeAndValue &attr = name.Element(i); + + /* Print element separator */ + *p++ = '/'; + if (0 == --l) break; + + /* Print the type. */ + TPtr8 type(p, l); + type.Copy(attr.Type()); + p += type.Length(); + l -= type.Length(); + if (0 >= --l) break; + + /* Print equal sign */ + *p++ = '='; + if (0 == --l) break; + + /* Print the value. Let's just get the raw data here */ + TPtr8 value(p, l); + value.Copy(attr.EncodedValue().Mid(2)); + p += value.Length(); + l -= value.Length(); + if (0 >= --l) break; + } + + pj_str_t src; + pj_strset(&src, buf, buf_len - l); + + return src; +} + +/* Get certificate info from CX509Certificate. + */ +static void get_cert_info(pj_pool_t *pool, pj_ssl_cert_info *ci, + const CX509Certificate *x) +{ + enum { tmp_buf_len = 512 }; + char *tmp_buf; + unsigned len; + + pj_assert(pool && ci && x); + + /* Init */ + tmp_buf = new char[tmp_buf_len]; + pj_bzero(ci, sizeof(*ci)); + + /* Version */ + ci->version = x->Version(); + + /* Serial number */ + len = x->SerialNumber().Length(); + if (len > sizeof(ci->serial_no)) + len = sizeof(ci->serial_no); + pj_memcpy(ci->serial_no + sizeof(ci->serial_no) - len, + x->SerialNumber().Ptr(), len); + + /* Subject */ + { + HBufC *subject = NULL; + TRAPD(err, subject = x->SubjectL()); + if (err == KErrNone) { + TPtr16 ptr16(subject->Des()); + len = ptr16.Length(); + TPtr8 ptr8((TUint8*)pj_pool_alloc(pool, len), len); + ptr8.Copy(ptr16); + pj_strset(&ci->subject.cn, (char*)ptr8.Ptr(), ptr8.Length()); + } + pj_str_t tmp = get_cert_name(tmp_buf, tmp_buf_len, + x->SubjectName()); + pj_strdup(pool, &ci->subject.info, &tmp); + } + + /* Issuer */ + { + HBufC *issuer = NULL; + TRAPD(err, issuer = x->IssuerL()); + if (err == KErrNone) { + TPtr16 ptr16(issuer->Des()); + len = ptr16.Length(); + TPtr8 ptr8((TUint8*)pj_pool_alloc(pool, len), len); + ptr8.Copy(ptr16); + pj_strset(&ci->issuer.cn, (char*)ptr8.Ptr(), ptr8.Length()); + } + pj_str_t tmp = get_cert_name(tmp_buf, tmp_buf_len, + x->IssuerName()); + pj_strdup(pool, &ci->issuer.info, &tmp); + } + + /* Validity */ + const CValidityPeriod &valid_period = x->ValidityPeriod(); + TTime base_time(TDateTime(1970, EJanuary, 0, 0, 0, 0, 0)); + TTimeIntervalSeconds tmp_sec; + valid_period.Start().SecondsFrom(base_time, tmp_sec); + ci->validity.start.sec = tmp_sec.Int(); + valid_period.Finish().SecondsFrom(base_time, tmp_sec); + ci->validity.end.sec = tmp_sec.Int(); + + /* Deinit */ + delete [] tmp_buf; +} + + +/* Update certificates info. This function should be called after handshake + * or renegotiation successfully completed. + */ +static void update_certs_info(pj_ssl_sock_t *ssock) +{ + const CX509Certificate *x; + + pj_assert(ssock && ssock->sock && + ssock->sock->GetState() == CPjSSLSocket::SSL_STATE_ESTABLISHED); + + /* Active remote certificate */ + x = ssock->sock->GetPeerCert(); + if (x) { + get_cert_info(ssock->pool, &ssock->remote_cert_info, x); + } else { + pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info)); + } +} + + +/* Available ciphers */ +static unsigned ciphers_num_ = 0; +static struct ciphers_t +{ + pj_ssl_cipher id; + const char *name; +} ciphers_[64]; + +/* + * Get cipher list supported by SSL/TLS backend. + */ +PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables (pj_ssl_cipher ciphers[], + unsigned *cipher_num) +{ + unsigned i; + + PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL); + + if (ciphers_num_ == 0) { + RSocket sock; + CSecureSocket *secure_sock; + TPtrC16 proto(_L16("TLS1.0")); + + secure_sock = CSecureSocket::NewL(sock, proto); + if (secure_sock) { + TBuf8<128> ciphers_buf(0); + secure_sock->AvailableCipherSuites(ciphers_buf); + + ciphers_num_ = ciphers_buf.Length() / 2; + if (ciphers_num_ > PJ_ARRAY_SIZE(ciphers_)) + ciphers_num_ = PJ_ARRAY_SIZE(ciphers_); + for (i = 0; i < ciphers_num_; ++i) { + ciphers_[i].id = (pj_ssl_cipher)(ciphers_buf[i*2]*10 + + ciphers_buf[i*2+1]); + ciphers_[i].name = get_cipher_name(ciphers_[i].id); + } + } + + delete secure_sock; + } + + if (ciphers_num_ == 0) { + *cipher_num = 0; + return PJ_ENOTFOUND; + } + + *cipher_num = PJ_MIN(*cipher_num, ciphers_num_); + for (i = 0; i < *cipher_num; ++i) + ciphers[i] = ciphers_[i].id; + + return PJ_SUCCESS; +} + + +/* Get cipher name string */ +PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher) +{ + unsigned i; + + if (ciphers_num_ == 0) { + pj_ssl_cipher c[1]; + i = 0; + pj_ssl_cipher_get_availables(c, &i); + } + + for (i = 0; i < ciphers_num_; ++i) { + if (cipher == ciphers_[i].id) + return ciphers_[i].name; + } + + return NULL; +} + + +/* Check if the specified cipher is supported by SSL/TLS backend. */ +PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher) +{ + unsigned i; + + if (ciphers_num_ == 0) { + pj_ssl_cipher c[1]; + i = 0; + pj_ssl_cipher_get_availables(c, &i); + } + + for (i = 0; i < ciphers_num_; ++i) { + if (cipher == ciphers_[i].id) + return PJ_TRUE; + } + + return PJ_FALSE; +} + + +/* + * Create SSL socket instance. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool, + const pj_ssl_sock_param *param, + pj_ssl_sock_t **p_ssock) +{ + pj_ssl_sock_t *ssock; + + PJ_ASSERT_RETURN(param->async_cnt == 1, PJ_EINVAL); + PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL); + + /* Allocate secure socket */ + ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t); + + /* Allocate write buffer */ + ssock->write_state.buf = (char*)pj_pool_alloc(pool, + param->send_buffer_size); + ssock->write_state.max_len = param->send_buffer_size; + ssock->write_state.start = ssock->write_state.buf; + + /* Init secure socket */ + ssock->pool = pool; + ssock->sock_af = param->sock_af; + ssock->sock_type = param->sock_type; + ssock->cb = param->cb; + ssock->user_data = param->user_data; + ssock->timeout = param->timeout; + if (param->ciphers_num > 0) { + /* Cipher list in Symbian is represented as array of two-octets. */ + ssock->ciphers.slen = param->ciphers_num*2; + ssock->ciphers.ptr = (char*)pj_pool_alloc(pool, ssock->ciphers.slen); + pj_uint8_t *c = (pj_uint8_t*)ssock->ciphers.ptr; + for (unsigned i = 0; i < param->ciphers_num; ++i) { + *c++ = (pj_uint8_t)(param->ciphers[i] & 0xFF00) >> 8; + *c++ = (pj_uint8_t)(param->ciphers[i] & 0xFF); + } + } + pj_strdup_with_null(pool, &ssock->servername, ¶m->server_name); + + ssock->qos_type = param->qos_type; + ssock->qos_ignore_error = param->qos_ignore_error; + pj_memcpy(&ssock->qos_params, ¶m->qos_params, + sizeof(param->qos_params)); + + /* Finally */ + *p_ssock = ssock; + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool, + const pj_str_t *CA_file, + const pj_str_t *cert_file, + const pj_str_t *privkey_file, + const pj_str_t *privkey_pass, + pj_ssl_cert_t **p_cert) +{ + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(CA_file); + PJ_UNUSED_ARG(cert_file); + PJ_UNUSED_ARG(privkey_file); + PJ_UNUSED_ARG(privkey_pass); + PJ_UNUSED_ARG(p_cert); + return PJ_ENOTSUP; +} + +/* + * Set SSL socket credential. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate( + pj_ssl_sock_t *ssock, + pj_pool_t *pool, + const pj_ssl_cert_t *cert) +{ + PJ_UNUSED_ARG(ssock); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(cert); + return PJ_ENOTSUP; +} + +/* + * Close the SSL socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock) +{ + PJ_ASSERT_RETURN(ssock, PJ_EINVAL); + + delete ssock->connect_timer; + ssock->connect_timer = NULL; + + delete ssock->sock; + ssock->sock = NULL; + + delete ssock->read_state.read_buf; + delete ssock->read_state.orig_buf; + ssock->read_state.read_buf = NULL; + ssock->read_state.orig_buf = NULL; + + return PJ_SUCCESS; +} + + +/* + * Associate arbitrary data with the SSL socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data (pj_ssl_sock_t *ssock, + void *user_data) +{ + PJ_ASSERT_RETURN(ssock, PJ_EINVAL); + + ssock->user_data = user_data; + + return PJ_SUCCESS; +} + + +/* + * Retrieve the user data previously associated with this SSL + * socket. + */ +PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock) +{ + PJ_ASSERT_RETURN(ssock, NULL); + + return ssock->user_data; +} + + +/* + * Retrieve the local address and port used by specified SSL socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock, + pj_ssl_sock_info *info) +{ + PJ_ASSERT_RETURN(ssock && info, PJ_EINVAL); + + pj_bzero(info, sizeof(*info)); + + info->established = ssock->established; + + /* Local address */ + if (ssock->sock) { + const TInetAddr* local_addr_ = ssock->sock->GetLocalAddr(); + int addrlen = sizeof(pj_sockaddr); + pj_status_t status; + + status = PjSymbianOS::Addr2pj(*local_addr_, info->local_addr, &addrlen); + if (status != PJ_SUCCESS) + return status; + } else { + pj_sockaddr_cp(&info->local_addr, &ssock->local_addr); + } + + if (info->established) { + /* Cipher suite */ + TBuf8<4> cipher; + if (ssock->sock->GetCipher(cipher) == KErrNone) { + info->cipher = (pj_ssl_cipher)cipher[1]; + } + + /* Remote address */ + pj_sockaddr_cp((pj_sockaddr_t*)&info->remote_addr, + (pj_sockaddr_t*)&ssock->rem_addr); + + /* Certificates info */ + info->remote_cert_info = &ssock->remote_cert_info; + } + + /* Protocol */ + info->proto = ssock->proto; + + return PJ_SUCCESS; +} + + +/* + * Starts read operation on this SSL socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_read (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + unsigned buff_size, + pj_uint32_t flags) +{ + PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL); + PJ_ASSERT_RETURN(ssock->established, PJ_EINVALIDOP); + + /* Reading is already started */ + if (ssock->read_state.orig_buf) { + return PJ_SUCCESS; + } + + void *readbuf[1]; + readbuf[0] = pj_pool_alloc(pool, buff_size); + return pj_ssl_sock_start_read2(ssock, pool, buff_size, readbuf, flags); +} + +static void read_cb(int err, void *key) +{ + pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key; + pj_status_t status; + + status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err); + + /* Check connection status */ + if (err == KErrEof || !PjSymbianOS::Instance()->IsConnectionUp() || + !ssock->established) + { + status = PJ_EEOF; + } + + /* Notify data arrival */ + if (ssock->cb.on_data_read) { + pj_size_t remainder = 0; + char *data = (char*)ssock->read_state.orig_buf->Ptr(); + pj_size_t data_len = ssock->read_state.read_buf->Length() + + ssock->read_state.read_buf->Ptr() - + ssock->read_state.orig_buf->Ptr(); + + if (data_len > 0) { + /* Notify received data */ + pj_bool_t ret = (*ssock->cb.on_data_read)(ssock, data, data_len, + status, &remainder); + if (!ret) { + /* We've been destroyed */ + return; + } + + /* Calculate available data for next READ operation */ + if (remainder > 0) { + pj_size_t data_maxlen = ssock->read_state.orig_buf->MaxLength(); + + /* There is some data left unconsumed by application, we give + * smaller buffer for next READ operation. + */ + ssock->read_state.read_buf->Set((TUint8*)data+remainder, 0, + data_maxlen - remainder); + } else { + /* Give all buffer for next READ operation. + */ + ssock->read_state.read_buf->Set(*ssock->read_state.orig_buf); + } + } + } + + if (status == PJ_SUCCESS) { + /* Perform the "next" READ operation */ + CPjSSLSocketReader *reader = ssock->sock->GetReader(); + ssock->read_state.read_buf->SetLength(0); + status = reader->Read(&read_cb, ssock, *ssock->read_state.read_buf, + ssock->read_state.flags); + } + + /* Connection closed or something goes wrong */ + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + /* Notify error */ + if (ssock->cb.on_data_read) { + pj_bool_t ret = (*ssock->cb.on_data_read)(ssock, NULL, 0, + status, NULL); + if (!ret) { + /* We've been destroyed */ + return; + } + } + + delete ssock->read_state.read_buf; + delete ssock->read_state.orig_buf; + ssock->read_state.read_buf = NULL; + ssock->read_state.orig_buf = NULL; + ssock->established = PJ_FALSE; + } +} + +/* + * Same as #pj_ssl_sock_start_read(), except that the application + * supplies the buffers for the read operation so that the acive socket + * does not have to allocate the buffers. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + unsigned buff_size, + void *readbuf[], + pj_uint32_t flags) +{ + PJ_ASSERT_RETURN(ssock && buff_size && readbuf, PJ_EINVAL); + PJ_ASSERT_RETURN(ssock->established, PJ_EINVALIDOP); + + /* Return failure if access point is marked as down by app. */ + PJ_SYMBIAN_CHECK_CONNECTION(); + + /* Reading is already started */ + if (ssock->read_state.orig_buf) { + return PJ_SUCCESS; + } + + PJ_UNUSED_ARG(pool); + + /* Get reader instance */ + CPjSSLSocketReader *reader = ssock->sock->GetReader(); + if (!reader) + return PJ_ENOMEM; + + /* We manage two buffer pointers here: + * 1. orig_buf keeps the orginal buffer address (and its max length). + * 2. read_buf provides buffer for READ operation, mind that there may be + * some remainder data left by application. + */ + ssock->read_state.read_buf = new TPtr8((TUint8*)readbuf[0], 0, buff_size); + ssock->read_state.orig_buf = new TPtr8((TUint8*)readbuf[0], 0, buff_size); + ssock->read_state.flags = flags; + + pj_status_t status; + status = reader->Read(&read_cb, ssock, *ssock->read_state.read_buf, + ssock->read_state.flags); + + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + delete ssock->read_state.read_buf; + delete ssock->read_state.orig_buf; + ssock->read_state.read_buf = NULL; + ssock->read_state.orig_buf = NULL; + + return status; + } + + return PJ_SUCCESS; +} + +/* + * Same as pj_ssl_sock_start_read(), except that this function is used + * only for datagram sockets, and it will trigger \a on_data_recvfrom() + * callback instead. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + unsigned buff_size, + pj_uint32_t flags) +{ + PJ_UNUSED_ARG(ssock); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(buff_size); + PJ_UNUSED_ARG(flags); + return PJ_ENOTSUP; +} + +/* + * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() + * operation takes the buffer from the argument rather than creating + * new ones. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + unsigned buff_size, + void *readbuf[], + pj_uint32_t flags) +{ + PJ_UNUSED_ARG(ssock); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(buff_size); + PJ_UNUSED_ARG(readbuf); + PJ_UNUSED_ARG(flags); + return PJ_ENOTSUP; +} + +static void send_cb(int err, void *key) +{ + pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key; + write_state_t *st = &ssock->write_state; + + /* Check connection status */ + if (err != KErrNone || !PjSymbianOS::Instance()->IsConnectionUp() || + !ssock->established) + { + ssock->established = PJ_FALSE; + return; + } + + /* Remove sent data from buffer */ + st->start += st->current_data->len; + st->len -= st->current_data->len; + + /* Reset current outstanding send */ + st->current_data = NULL; + + /* Let's check if there is pending data to send */ + if (st->len) { + write_data_t *wdata = (write_data_t*)st->start; + pj_status_t status; + + st->send_ptr.Set((TUint8*)wdata->data, (TInt)wdata->data_len); + st->current_data = wdata; + status = ssock->sock->Send(&send_cb, ssock, st->send_ptr, 0); + if (status != PJ_EPENDING) { + ssock->established = PJ_FALSE; + st->len = 0; + return; + } + } else { + /* Buffer empty, reset the start position */ + st->start = st->buf; + } +} + +/* + * Send data using the socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock, + pj_ioqueue_op_key_t *send_key, + const void *data, + pj_ssize_t *size, + unsigned flags) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(ssock && data && size, PJ_EINVAL); + PJ_ASSERT_RETURN(ssock->write_state.max_len == 0 || + ssock->write_state.max_len >= (pj_size_t)*size, + PJ_ETOOSMALL); + + /* Check connection status */ + if (!PjSymbianOS::Instance()->IsConnectionUp() || !ssock->established) + { + ssock->established = PJ_FALSE; + return PJ_ECANCELLED; + } + + write_state_t *st = &ssock->write_state; + + /* Synchronous mode */ + if (st->max_len == 0) { + st->send_ptr.Set((TUint8*)data, (TInt)*size); + return ssock->sock->SendSync(st->send_ptr, flags); + } + + /* CSecureSocket only allows one outstanding send operation, so + * we use buffering mechanism to allow application to perform send + * operations at any time. + */ + + pj_size_t needed_len = *size + sizeof(write_data_t) - 1; + + /* Align needed_len to be multiplication of 4 */ + needed_len = ((needed_len + 3) >> 2) << 2; + + /* Block until there is buffer slot available and contiguous! */ + while (st->start + st->len + needed_len > st->buf + st->max_len) { + pj_symbianos_poll(-1, -1); + } + + /* Push back the send data into the buffer */ + write_data_t *wdata = (write_data_t*)(st->start + st->len); + + wdata->len = needed_len; + wdata->key = send_key; + wdata->data_len = (pj_size_t)*size; + pj_memcpy(wdata->data, data, *size); + st->len += needed_len; + + /* If no outstanding send, send it */ + if (st->current_data == NULL) { + pj_status_t status; + + wdata = (write_data_t*)st->start; + st->current_data = wdata; + st->send_ptr.Set((TUint8*)wdata->data, (TInt)wdata->data_len); + status = ssock->sock->Send(&send_cb, ssock, st->send_ptr, flags); + + if (status != PJ_EPENDING) { + *size = -status; + return status; + } + } + + return PJ_SUCCESS; +} + +/* + * Send datagram using the socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock, + pj_ioqueue_op_key_t *send_key, + const void *data, + pj_ssize_t *size, + unsigned flags, + const pj_sockaddr_t *addr, + int addr_len) +{ + PJ_UNUSED_ARG(ssock); + PJ_UNUSED_ARG(send_key); + PJ_UNUSED_ARG(data); + PJ_UNUSED_ARG(size); + PJ_UNUSED_ARG(flags); + PJ_UNUSED_ARG(addr); + PJ_UNUSED_ARG(addr_len); + return PJ_ENOTSUP; +} + +/* + * Starts asynchronous socket accept() operations on this SSL socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + const pj_sockaddr_t *local_addr, + int addr_len) +{ + PJ_UNUSED_ARG(ssock); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(local_addr); + PJ_UNUSED_ARG(addr_len); + + return PJ_ENOTSUP; +} + +static void connect_cb(int err, void *key) +{ + pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key; + pj_status_t status; + + if (ssock->connect_timer) { + delete ssock->connect_timer; + ssock->connect_timer = NULL; + } + + status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err); + if (status == PJ_SUCCESS) { + ssock->established = PJ_TRUE; + update_certs_info(ssock); + } else { + delete ssock->sock; + ssock->sock = NULL; + if (err == KErrTimedOut) status = PJ_ETIMEDOUT; + } + + if (ssock->cb.on_connect_complete) { + pj_bool_t ret = (*ssock->cb.on_connect_complete)(ssock, status); + if (!ret) { + /* We've been destroyed */ + return; + } + } +} + +static void connect_timer_cb(void *key) +{ + connect_cb(KErrTimedOut, key); +} + +/* + * Starts asynchronous socket connect() operation and SSL/TLS handshaking + * for this socket. Once the connection is done (either successfully or not), + * the \a on_connect_complete() callback will be called. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_connect (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + const pj_sockaddr_t *localaddr, + const pj_sockaddr_t *remaddr, + int addr_len) +{ + CPjSSLSocket *sock = NULL; + pj_status_t status; + + PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len, + PJ_EINVAL); + + /* Check connection status */ + PJ_SYMBIAN_CHECK_CONNECTION(); + + if (ssock->sock != NULL) { + CPjSSLSocket::ssl_state state = ssock->sock->GetState(); + switch (state) { + case CPjSSLSocket::SSL_STATE_ESTABLISHED: + return PJ_SUCCESS; + default: + return PJ_EPENDING; + } + } + + /* Set SSL protocol */ + TPtrC8 proto; + + if (ssock->proto == PJ_SSL_SOCK_PROTO_DEFAULT) + ssock->proto = PJ_SSL_SOCK_PROTO_TLS1; + + /* CSecureSocket only support TLS1.0 and SSL3.0 */ + switch(ssock->proto) { + case PJ_SSL_SOCK_PROTO_TLS1: + proto.Set((const TUint8*)"TLS1.0", 6); + break; + case PJ_SSL_SOCK_PROTO_SSL3: + proto.Set((const TUint8*)"SSL3.0", 6); + break; + default: + return PJ_ENOTSUP; + } + + /* Prepare addresses */ + TInetAddr localaddr_, remaddr_; + status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)localaddr, addr_len, + localaddr_); + if (status != PJ_SUCCESS) + return status; + + status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)remaddr, addr_len, + remaddr_); + if (status != PJ_SUCCESS) + return status; + + pj_sockaddr_cp((pj_sockaddr_t*)&ssock->rem_addr, remaddr); + + /* Init SSL engine */ + TRAPD(err, sock = CPjSSLSocket::NewL(proto, ssock->qos_type, + ssock->qos_params)); + if (err != KErrNone) + return PJ_ENOMEM; + + if (ssock->timeout.sec != 0 || ssock->timeout.msec != 0) { + ssock->connect_timer = new CPjTimer(&ssock->timeout, + &connect_timer_cb, ssock); + } + + /* Convert server name to Symbian descriptor */ + TPtrC8 servername_((TUint8*)ssock->servername.ptr, + ssock->servername.slen); + + /* Convert cipher list to Symbian descriptor */ + TPtrC8 ciphers_((TUint8*)ssock->ciphers.ptr, + ssock->ciphers.slen); + + /* Try to connect */ + status = sock->Connect(&connect_cb, ssock, localaddr_, remaddr_, + servername_, ciphers_); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + delete sock; + return status; + } + + ssock->sock = sock; + return status; +} + + +PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock) +{ + PJ_UNUSED_ARG(ssock); + return PJ_ENOTSUP; +} diff --git a/pjlib/src/pj/string.c b/pjlib/src/pj/string.c new file mode 100644 index 0000000..799f492 --- /dev/null +++ b/pjlib/src/pj/string.c @@ -0,0 +1,202 @@ +/* $Id: string.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include + +#if PJ_FUNCTIONS_ARE_INLINED==0 +# include +#endif + + +PJ_DEF(char*) pj_strstr(const pj_str_t *str, const pj_str_t *substr) +{ + const char *s, *ends; + + /* Special case when substr is zero */ + if (substr->slen == 0) { + return (char*)str->ptr; + } + + s = str->ptr; + ends = str->ptr + str->slen - substr->slen; + for (; s<=ends; ++s) { + if (pj_ansi_strncmp(s, substr->ptr, substr->slen)==0) + return (char*)s; + } + return NULL; +} + + +PJ_DEF(char*) pj_stristr(const pj_str_t *str, const pj_str_t *substr) +{ + const char *s, *ends; + + /* Special case when substr is zero */ + if (substr->slen == 0) { + return (char*)str->ptr; + } + + s = str->ptr; + ends = str->ptr + str->slen - substr->slen; + for (; s<=ends; ++s) { + if (pj_ansi_strnicmp(s, substr->ptr, substr->slen)==0) + return (char*)s; + } + return NULL; +} + + +PJ_DEF(pj_str_t*) pj_strltrim( pj_str_t *str ) +{ + char *end = str->ptr + str->slen; + register char *p = str->ptr; + while (p < end && pj_isspace(*p)) + ++p; + str->slen -= (p - str->ptr); + str->ptr = p; + return str; +} + +PJ_DEF(pj_str_t*) pj_strrtrim( pj_str_t *str ) +{ + char *end = str->ptr + str->slen; + register char *p = end - 1; + while (p >= str->ptr && pj_isspace(*p)) + --p; + str->slen -= ((end - p) - 1); + return str; +} + +PJ_DEF(char*) pj_create_random_string(char *str, pj_size_t len) +{ + unsigned i; + char *p = str; + + PJ_CHECK_STACK(); + + for (i=0; i> 24, p+0 ); + pj_val_to_hex_digit( (val & 0x00FF0000) >> 16, p+2 ); + pj_val_to_hex_digit( (val & 0x0000FF00) >> 8, p+4 ); + pj_val_to_hex_digit( (val & 0x000000FF) >> 0, p+6 ); + p += 8; + } + for (i=i * 8; islen; ++i) { + if (!pj_isdigit(str->ptr[i])) + break; + value = value * 10 + (str->ptr[i] - '0'); + } + return value; +} + +PJ_DEF(unsigned long) pj_strtoul2(const pj_str_t *str, pj_str_t *endptr, + unsigned base) +{ + unsigned long value; + unsigned i; + + PJ_CHECK_STACK(); + + value = 0; + if (base <= 10) { + for (i=0; i<(unsigned)str->slen; ++i) { + unsigned c = (str->ptr[i] - '0'); + if (c >= base) + break; + value = value * base + c; + } + } else if (base == 16) { + for (i=0; i<(unsigned)str->slen; ++i) { + if (!pj_isxdigit(str->ptr[i])) + break; + value = value * 16 + pj_hex_digit_to_val(str->ptr[i]); + } + } else { + pj_assert(!"Unsupported base"); + i = 0; + value = 0xFFFFFFFFUL; + } + + if (endptr) { + endptr->ptr = str->ptr + i; + endptr->slen = str->slen - i; + } + + return value; +} + +PJ_DEF(int) pj_utoa(unsigned long val, char *buf) +{ + return pj_utoa_pad(val, buf, 0, 0); +} + +PJ_DEF(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad) +{ + char *p; + int len; + + PJ_CHECK_STACK(); + + p = buf; + do { + unsigned long digval = (unsigned long) (val % 10); + val /= 10; + *p++ = (char) (digval + '0'); + } while (val > 0); + + len = p-buf; + while (len < min_dig) { + *p++ = (char)pad; + ++len; + } + *p-- = '\0'; + + do { + char temp = *p; + *p = *buf; + *buf = temp; + --p; + ++buf; + } while (buf < p); + + return len; +} + + diff --git a/pjlib/src/pj/symbols.c b/pjlib/src/pj/symbols.c new file mode 100644 index 0000000..61e9526 --- /dev/null +++ b/pjlib/src/pj/symbols.c @@ -0,0 +1,348 @@ +/* $Id: symbols.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + +/* + * addr_resolv.h + */ +PJ_EXPORT_SYMBOL(pj_gethostbyname) + +/* + * array.h + */ +PJ_EXPORT_SYMBOL(pj_array_insert) +PJ_EXPORT_SYMBOL(pj_array_erase) +PJ_EXPORT_SYMBOL(pj_array_find) + +/* + * config.h + */ +PJ_EXPORT_SYMBOL(pj_dump_config) + +/* + * errno.h + */ +PJ_EXPORT_SYMBOL(pj_get_os_error) +PJ_EXPORT_SYMBOL(pj_set_os_error) +PJ_EXPORT_SYMBOL(pj_get_netos_error) +PJ_EXPORT_SYMBOL(pj_set_netos_error) +PJ_EXPORT_SYMBOL(pj_strerror) + +/* + * except.h + */ +PJ_EXPORT_SYMBOL(pj_throw_exception_) +PJ_EXPORT_SYMBOL(pj_push_exception_handler_) +PJ_EXPORT_SYMBOL(pj_pop_exception_handler_) +PJ_EXPORT_SYMBOL(pj_setjmp) +PJ_EXPORT_SYMBOL(pj_longjmp) +PJ_EXPORT_SYMBOL(pj_exception_id_alloc) +PJ_EXPORT_SYMBOL(pj_exception_id_free) +PJ_EXPORT_SYMBOL(pj_exception_id_name) + + +/* + * fifobuf.h + */ +PJ_EXPORT_SYMBOL(pj_fifobuf_init) +PJ_EXPORT_SYMBOL(pj_fifobuf_max_size) +PJ_EXPORT_SYMBOL(pj_fifobuf_alloc) +PJ_EXPORT_SYMBOL(pj_fifobuf_unalloc) +PJ_EXPORT_SYMBOL(pj_fifobuf_free) + +/* + * guid.h + */ +PJ_EXPORT_SYMBOL(pj_generate_unique_string) +PJ_EXPORT_SYMBOL(pj_create_unique_string) + +/* + * hash.h + */ +PJ_EXPORT_SYMBOL(pj_hash_calc) +PJ_EXPORT_SYMBOL(pj_hash_create) +PJ_EXPORT_SYMBOL(pj_hash_get) +PJ_EXPORT_SYMBOL(pj_hash_set) +PJ_EXPORT_SYMBOL(pj_hash_count) +PJ_EXPORT_SYMBOL(pj_hash_first) +PJ_EXPORT_SYMBOL(pj_hash_next) +PJ_EXPORT_SYMBOL(pj_hash_this) + +/* + * ioqueue.h + */ +PJ_EXPORT_SYMBOL(pj_ioqueue_create) +PJ_EXPORT_SYMBOL(pj_ioqueue_destroy) +PJ_EXPORT_SYMBOL(pj_ioqueue_set_lock) +PJ_EXPORT_SYMBOL(pj_ioqueue_register_sock) +PJ_EXPORT_SYMBOL(pj_ioqueue_unregister) +PJ_EXPORT_SYMBOL(pj_ioqueue_get_user_data) +PJ_EXPORT_SYMBOL(pj_ioqueue_poll) +PJ_EXPORT_SYMBOL(pj_ioqueue_read) +PJ_EXPORT_SYMBOL(pj_ioqueue_recv) +PJ_EXPORT_SYMBOL(pj_ioqueue_recvfrom) +PJ_EXPORT_SYMBOL(pj_ioqueue_write) +PJ_EXPORT_SYMBOL(pj_ioqueue_send) +PJ_EXPORT_SYMBOL(pj_ioqueue_sendto) +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 +PJ_EXPORT_SYMBOL(pj_ioqueue_accept) +PJ_EXPORT_SYMBOL(pj_ioqueue_connect) +#endif + +/* + * list.h + */ +PJ_EXPORT_SYMBOL(pj_list_insert_before) +PJ_EXPORT_SYMBOL(pj_list_insert_nodes_before) +PJ_EXPORT_SYMBOL(pj_list_insert_after) +PJ_EXPORT_SYMBOL(pj_list_insert_nodes_after) +PJ_EXPORT_SYMBOL(pj_list_merge_first) +PJ_EXPORT_SYMBOL(pj_list_merge_last) +PJ_EXPORT_SYMBOL(pj_list_erase) +PJ_EXPORT_SYMBOL(pj_list_find_node) +PJ_EXPORT_SYMBOL(pj_list_search) + + +/* + * log.h + */ +PJ_EXPORT_SYMBOL(pj_log_write) +#if PJ_LOG_MAX_LEVEL >= 1 +PJ_EXPORT_SYMBOL(pj_log_set_log_func) +PJ_EXPORT_SYMBOL(pj_log_get_log_func) +PJ_EXPORT_SYMBOL(pj_log_set_level) +PJ_EXPORT_SYMBOL(pj_log_get_level) +PJ_EXPORT_SYMBOL(pj_log_set_decor) +PJ_EXPORT_SYMBOL(pj_log_get_decor) +PJ_EXPORT_SYMBOL(pj_log_1) +#endif +#if PJ_LOG_MAX_LEVEL >= 2 +PJ_EXPORT_SYMBOL(pj_log_2) +#endif +#if PJ_LOG_MAX_LEVEL >= 3 +PJ_EXPORT_SYMBOL(pj_log_3) +#endif +#if PJ_LOG_MAX_LEVEL >= 4 +PJ_EXPORT_SYMBOL(pj_log_4) +#endif +#if PJ_LOG_MAX_LEVEL >= 5 +PJ_EXPORT_SYMBOL(pj_log_5) +#endif +#if PJ_LOG_MAX_LEVEL >= 6 +PJ_EXPORT_SYMBOL(pj_log_6) +#endif + +/* + * os.h + */ +PJ_EXPORT_SYMBOL(pj_init) +PJ_EXPORT_SYMBOL(pj_getpid) +PJ_EXPORT_SYMBOL(pj_thread_register) +PJ_EXPORT_SYMBOL(pj_thread_create) +PJ_EXPORT_SYMBOL(pj_thread_get_name) +PJ_EXPORT_SYMBOL(pj_thread_resume) +PJ_EXPORT_SYMBOL(pj_thread_this) +PJ_EXPORT_SYMBOL(pj_thread_join) +PJ_EXPORT_SYMBOL(pj_thread_destroy) +PJ_EXPORT_SYMBOL(pj_thread_sleep) +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0 +PJ_EXPORT_SYMBOL(pj_thread_check_stack) +PJ_EXPORT_SYMBOL(pj_thread_get_stack_max_usage) +PJ_EXPORT_SYMBOL(pj_thread_get_stack_info) +#endif +PJ_EXPORT_SYMBOL(pj_atomic_create) +PJ_EXPORT_SYMBOL(pj_atomic_destroy) +PJ_EXPORT_SYMBOL(pj_atomic_set) +PJ_EXPORT_SYMBOL(pj_atomic_get) +PJ_EXPORT_SYMBOL(pj_atomic_inc) +PJ_EXPORT_SYMBOL(pj_atomic_dec) +PJ_EXPORT_SYMBOL(pj_thread_local_alloc) +PJ_EXPORT_SYMBOL(pj_thread_local_free) +PJ_EXPORT_SYMBOL(pj_thread_local_set) +PJ_EXPORT_SYMBOL(pj_thread_local_get) +PJ_EXPORT_SYMBOL(pj_enter_critical_section) +PJ_EXPORT_SYMBOL(pj_leave_critical_section) +PJ_EXPORT_SYMBOL(pj_mutex_create) +PJ_EXPORT_SYMBOL(pj_mutex_lock) +PJ_EXPORT_SYMBOL(pj_mutex_unlock) +PJ_EXPORT_SYMBOL(pj_mutex_trylock) +PJ_EXPORT_SYMBOL(pj_mutex_destroy) +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 +PJ_EXPORT_SYMBOL(pj_mutex_is_locked) +#endif +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 +PJ_EXPORT_SYMBOL(pj_sem_create) +PJ_EXPORT_SYMBOL(pj_sem_wait) +PJ_EXPORT_SYMBOL(pj_sem_trywait) +PJ_EXPORT_SYMBOL(pj_sem_post) +PJ_EXPORT_SYMBOL(pj_sem_destroy) +#endif +PJ_EXPORT_SYMBOL(pj_gettimeofday) +PJ_EXPORT_SYMBOL(pj_time_decode) +#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 +PJ_EXPORT_SYMBOL(pj_gettickcount) +PJ_EXPORT_SYMBOL(pj_get_timestamp) +PJ_EXPORT_SYMBOL(pj_get_timestamp_freq) +PJ_EXPORT_SYMBOL(pj_elapsed_time) +PJ_EXPORT_SYMBOL(pj_elapsed_usec) +PJ_EXPORT_SYMBOL(pj_elapsed_nanosec) +PJ_EXPORT_SYMBOL(pj_elapsed_cycle) +#endif + + +/* + * pool.h + */ +PJ_EXPORT_SYMBOL(pj_pool_create) +PJ_EXPORT_SYMBOL(pj_pool_release) +PJ_EXPORT_SYMBOL(pj_pool_getobjname) +PJ_EXPORT_SYMBOL(pj_pool_reset) +PJ_EXPORT_SYMBOL(pj_pool_get_capacity) +PJ_EXPORT_SYMBOL(pj_pool_get_used_size) +PJ_EXPORT_SYMBOL(pj_pool_alloc) +PJ_EXPORT_SYMBOL(pj_pool_calloc) +PJ_EXPORT_SYMBOL(pj_pool_factory_default_policy) +PJ_EXPORT_SYMBOL(pj_pool_create_int) +PJ_EXPORT_SYMBOL(pj_pool_init_int) +PJ_EXPORT_SYMBOL(pj_pool_destroy_int) +PJ_EXPORT_SYMBOL(pj_caching_pool_init) +PJ_EXPORT_SYMBOL(pj_caching_pool_destroy) + +/* + * rand.h + */ +PJ_EXPORT_SYMBOL(pj_rand) +PJ_EXPORT_SYMBOL(pj_srand) + +/* + * rbtree.h + */ +PJ_EXPORT_SYMBOL(pj_rbtree_init) +PJ_EXPORT_SYMBOL(pj_rbtree_first) +PJ_EXPORT_SYMBOL(pj_rbtree_last) +PJ_EXPORT_SYMBOL(pj_rbtree_next) +PJ_EXPORT_SYMBOL(pj_rbtree_prev) +PJ_EXPORT_SYMBOL(pj_rbtree_insert) +PJ_EXPORT_SYMBOL(pj_rbtree_find) +PJ_EXPORT_SYMBOL(pj_rbtree_erase) +PJ_EXPORT_SYMBOL(pj_rbtree_max_height) +PJ_EXPORT_SYMBOL(pj_rbtree_min_height) + +/* + * sock.h + */ +PJ_EXPORT_SYMBOL(PJ_AF_UNIX) +PJ_EXPORT_SYMBOL(PJ_AF_INET) +PJ_EXPORT_SYMBOL(PJ_AF_INET6) +PJ_EXPORT_SYMBOL(PJ_AF_PACKET) +PJ_EXPORT_SYMBOL(PJ_AF_IRDA) +PJ_EXPORT_SYMBOL(PJ_SOCK_STREAM) +PJ_EXPORT_SYMBOL(PJ_SOCK_DGRAM) +PJ_EXPORT_SYMBOL(PJ_SOCK_RAW) +PJ_EXPORT_SYMBOL(PJ_SOCK_RDM) +PJ_EXPORT_SYMBOL(PJ_SOL_SOCKET) +PJ_EXPORT_SYMBOL(PJ_SOL_IP) +PJ_EXPORT_SYMBOL(PJ_SOL_TCP) +PJ_EXPORT_SYMBOL(PJ_SOL_UDP) +PJ_EXPORT_SYMBOL(PJ_SOL_IPV6) +PJ_EXPORT_SYMBOL(pj_ntohs) +PJ_EXPORT_SYMBOL(pj_htons) +PJ_EXPORT_SYMBOL(pj_ntohl) +PJ_EXPORT_SYMBOL(pj_htonl) +PJ_EXPORT_SYMBOL(pj_inet_ntoa) +PJ_EXPORT_SYMBOL(pj_inet_aton) +PJ_EXPORT_SYMBOL(pj_inet_addr) +PJ_EXPORT_SYMBOL(pj_sockaddr_in_set_str_addr) +PJ_EXPORT_SYMBOL(pj_sockaddr_in_init) +PJ_EXPORT_SYMBOL(pj_gethostname) +PJ_EXPORT_SYMBOL(pj_gethostaddr) +PJ_EXPORT_SYMBOL(pj_sock_socket) +PJ_EXPORT_SYMBOL(pj_sock_close) +PJ_EXPORT_SYMBOL(pj_sock_bind) +PJ_EXPORT_SYMBOL(pj_sock_bind_in) +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 +PJ_EXPORT_SYMBOL(pj_sock_listen) +PJ_EXPORT_SYMBOL(pj_sock_accept) +PJ_EXPORT_SYMBOL(pj_sock_shutdown) +#endif +PJ_EXPORT_SYMBOL(pj_sock_connect) +PJ_EXPORT_SYMBOL(pj_sock_getpeername) +PJ_EXPORT_SYMBOL(pj_sock_getsockname) +PJ_EXPORT_SYMBOL(pj_sock_getsockopt) +PJ_EXPORT_SYMBOL(pj_sock_setsockopt) +PJ_EXPORT_SYMBOL(pj_sock_recv) +PJ_EXPORT_SYMBOL(pj_sock_recvfrom) +PJ_EXPORT_SYMBOL(pj_sock_send) +PJ_EXPORT_SYMBOL(pj_sock_sendto) + +/* + * sock_select.h + */ +PJ_EXPORT_SYMBOL(PJ_FD_ZERO) +PJ_EXPORT_SYMBOL(PJ_FD_SET) +PJ_EXPORT_SYMBOL(PJ_FD_CLR) +PJ_EXPORT_SYMBOL(PJ_FD_ISSET) +PJ_EXPORT_SYMBOL(pj_sock_select) + +/* + * string.h + */ +PJ_EXPORT_SYMBOL(pj_str) +PJ_EXPORT_SYMBOL(pj_strassign) +PJ_EXPORT_SYMBOL(pj_strcpy) +PJ_EXPORT_SYMBOL(pj_strcpy2) +PJ_EXPORT_SYMBOL(pj_strdup) +PJ_EXPORT_SYMBOL(pj_strdup_with_null) +PJ_EXPORT_SYMBOL(pj_strdup2) +PJ_EXPORT_SYMBOL(pj_strdup3) +PJ_EXPORT_SYMBOL(pj_strcmp) +PJ_EXPORT_SYMBOL(pj_strcmp2) +PJ_EXPORT_SYMBOL(pj_strncmp) +PJ_EXPORT_SYMBOL(pj_strncmp2) +PJ_EXPORT_SYMBOL(pj_stricmp) +PJ_EXPORT_SYMBOL(pj_stricmp2) +PJ_EXPORT_SYMBOL(pj_strnicmp) +PJ_EXPORT_SYMBOL(pj_strnicmp2) +PJ_EXPORT_SYMBOL(pj_strcat) +PJ_EXPORT_SYMBOL(pj_strltrim) +PJ_EXPORT_SYMBOL(pj_strrtrim) +PJ_EXPORT_SYMBOL(pj_strtrim) +PJ_EXPORT_SYMBOL(pj_create_random_string) +PJ_EXPORT_SYMBOL(pj_strtoul) +PJ_EXPORT_SYMBOL(pj_utoa) +PJ_EXPORT_SYMBOL(pj_utoa_pad) + +/* + * timer.h + */ +PJ_EXPORT_SYMBOL(pj_timer_heap_mem_size) +PJ_EXPORT_SYMBOL(pj_timer_heap_create) +PJ_EXPORT_SYMBOL(pj_timer_entry_init) +PJ_EXPORT_SYMBOL(pj_timer_heap_schedule) +PJ_EXPORT_SYMBOL(pj_timer_heap_cancel) +PJ_EXPORT_SYMBOL(pj_timer_heap_count) +PJ_EXPORT_SYMBOL(pj_timer_heap_earliest_time) +PJ_EXPORT_SYMBOL(pj_timer_heap_poll) + +/* + * types.h + */ +PJ_EXPORT_SYMBOL(pj_time_val_normalize) + diff --git a/pjlib/src/pj/timer.c b/pjlib/src/pj/timer.c new file mode 100644 index 0000000..e78cba3 --- /dev/null +++ b/pjlib/src/pj/timer.c @@ -0,0 +1,610 @@ +/* $Id: timer.c 4154 2012-06-05 10:41:17Z bennylp $ */ +/* + * The PJLIB's timer heap is based (or more correctly, copied and modied) + * from ACE library by Douglas C. Schmidt. ACE is an excellent OO framework + * that implements many core patterns for concurrent communication software. + * If you're looking for C++ alternative of PJLIB, then ACE is your best + * solution. + * + * You may use this file according to ACE open source terms or PJLIB open + * source terms. You can find the fine ACE library at: + * http://www.cs.wustl.edu/~schmidt/ACE.html + * + * ACE is Copyright (C)1993-2006 Douglas C. Schmidt + * + * GNU Public License: + * 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 +#include +#include +#include +#include +#include +#include +#include + +#define THIS_FILE "timer.c" + +#define HEAP_PARENT(X) (X == 0 ? 0 : (((X) - 1) / 2)) +#define HEAP_LEFT(X) (((X)+(X))+1) + + +#define DEFAULT_MAX_TIMED_OUT_PER_POLL (64) + + +/** + * The implementation of timer heap. + */ +struct pj_timer_heap_t +{ + /** Pool from which the timer heap resize will get the storage from */ + pj_pool_t *pool; + + /** Maximum size of the heap. */ + pj_size_t max_size; + + /** Current size of the heap. */ + pj_size_t cur_size; + + /** Max timed out entries to process per poll. */ + unsigned max_entries_per_poll; + + /** Lock object. */ + pj_lock_t *lock; + + /** Autodelete lock. */ + pj_bool_t auto_delete_lock; + + /** + * Current contents of the Heap, which is organized as a "heap" of + * pj_timer_entry *'s. In this context, a heap is a "partially + * ordered, almost complete" binary tree, which is stored in an + * array. + */ + pj_timer_entry **heap; + + /** + * An array of "pointers" that allows each pj_timer_entry in the + * to be located in O(1) time. Basically, + * contains the slot in the array where an pj_timer_entry + * with timer id resides. Thus, the timer id passed back from + * is really an slot into the array. The + * array serves two purposes: negative values are + * treated as "pointers" for the , whereas positive + * values are treated as "pointers" into the array. + */ + pj_timer_id_t *timer_ids; + + /** + * "Pointer" to the first element in the freelist contained within + * the array, which is organized as a stack. + */ + pj_timer_id_t timer_ids_freelist; + + /** Callback to be called when a timer expires. */ + pj_timer_heap_callback *callback; + +}; + + + +PJ_INLINE(void) lock_timer_heap( pj_timer_heap_t *ht ) +{ + if (ht->lock) { + pj_lock_acquire(ht->lock); + } +} + +PJ_INLINE(void) unlock_timer_heap( pj_timer_heap_t *ht ) +{ + if (ht->lock) { + pj_lock_release(ht->lock); + } +} + + +static void copy_node( pj_timer_heap_t *ht, int slot, pj_timer_entry *moved_node ) +{ + PJ_CHECK_STACK(); + + // Insert into its new location in the heap. + ht->heap[slot] = moved_node; + + // Update the corresponding slot in the parallel array. + ht->timer_ids[moved_node->_timer_id] = slot; +} + +static pj_timer_id_t pop_freelist( pj_timer_heap_t *ht ) +{ + // We need to truncate this to for backwards compatibility. + pj_timer_id_t new_id = ht->timer_ids_freelist; + + PJ_CHECK_STACK(); + + // The freelist values in the are negative, so we need + // to negate them to get the next freelist "pointer." + ht->timer_ids_freelist = + -ht->timer_ids[ht->timer_ids_freelist]; + + return new_id; + +} + +static void push_freelist (pj_timer_heap_t *ht, pj_timer_id_t old_id) +{ + PJ_CHECK_STACK(); + + // The freelist values in the are negative, so we need + // to negate them to get the next freelist "pointer." + ht->timer_ids[old_id] = -ht->timer_ids_freelist; + ht->timer_ids_freelist = old_id; +} + + +static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node, + size_t slot, size_t child) +{ + PJ_CHECK_STACK(); + + // Restore the heap property after a deletion. + + while (child < ht->cur_size) + { + // Choose the smaller of the two children. + if (child + 1 < ht->cur_size + && PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value, ht->heap[child]->_timer_value)) + child++; + + // Perform a if the child has a larger timeout value than + // the . + if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value, moved_node->_timer_value)) + { + copy_node( ht, slot, ht->heap[child]); + slot = child; + child = HEAP_LEFT(child); + } + else + // We've found our location in the heap. + break; + } + + copy_node( ht, slot, moved_node); +} + +static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node, + size_t slot, size_t parent) +{ + // Restore the heap property after an insertion. + + while (slot > 0) + { + // If the parent node is greater than the we need + // to copy it down. + if (PJ_TIME_VAL_LT(moved_node->_timer_value, ht->heap[parent]->_timer_value)) + { + copy_node(ht, slot, ht->heap[parent]); + slot = parent; + parent = HEAP_PARENT(slot); + } + else + break; + } + + // Insert the new node into its proper resting place in the heap and + // update the corresponding slot in the parallel array. + copy_node(ht, slot, moved_node); +} + + +static pj_timer_entry * remove_node( pj_timer_heap_t *ht, size_t slot) +{ + pj_timer_entry *removed_node = ht->heap[slot]; + + // Return this timer id to the freelist. + push_freelist( ht, removed_node->_timer_id ); + + // Decrement the size of the heap by one since we're removing the + // "slot"th node. + ht->cur_size--; + + // Set the ID + removed_node->_timer_id = -1; + + // Only try to reheapify if we're not deleting the last entry. + + if (slot < ht->cur_size) + { + int parent; + pj_timer_entry *moved_node = ht->heap[ht->cur_size]; + + // Move the end node to the location being removed and update + // the corresponding slot in the parallel array. + copy_node( ht, slot, moved_node); + + // If the time_value_> is great than or equal its + // parent it needs be moved down the heap. + parent = HEAP_PARENT (slot); + + if (PJ_TIME_VAL_GTE(moved_node->_timer_value, ht->heap[parent]->_timer_value)) + reheap_down( ht, moved_node, slot, HEAP_LEFT(slot)); + else + reheap_up( ht, moved_node, slot, parent); + } + + return removed_node; +} + +static void grow_heap(pj_timer_heap_t *ht) +{ + // All the containers will double in size from max_size_ + size_t new_size = ht->max_size * 2; + pj_timer_id_t *new_timer_ids; + pj_size_t i; + + // First grow the heap itself. + + pj_timer_entry **new_heap = 0; + + new_heap = (pj_timer_entry**) + pj_pool_alloc(ht->pool, sizeof(pj_timer_entry*) * new_size); + memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry*)); + //delete [] this->heap_; + ht->heap = new_heap; + + // Grow the array of timer ids. + + new_timer_ids = 0; + new_timer_ids = (pj_timer_id_t*) + pj_pool_alloc(ht->pool, new_size * sizeof(pj_timer_id_t)); + + memcpy( new_timer_ids, ht->timer_ids, ht->max_size * sizeof(pj_timer_id_t)); + + //delete [] timer_ids_; + ht->timer_ids = new_timer_ids; + + // And add the new elements to the end of the "freelist". + for (i = ht->max_size; i < new_size; i++) + ht->timer_ids[i] = -((pj_timer_id_t) (i + 1)); + + ht->max_size = new_size; +} + +static void insert_node(pj_timer_heap_t *ht, pj_timer_entry *new_node) +{ + if (ht->cur_size + 2 >= ht->max_size) + grow_heap(ht); + + reheap_up( ht, new_node, ht->cur_size, HEAP_PARENT(ht->cur_size)); + ht->cur_size++; +} + + +static pj_status_t schedule_entry( pj_timer_heap_t *ht, + pj_timer_entry *entry, + const pj_time_val *future_time ) +{ + if (ht->cur_size < ht->max_size) + { + // Obtain the next unique sequence number. + // Set the entry + entry->_timer_id = pop_freelist(ht); + entry->_timer_value = *future_time; + insert_node( ht, entry); + return 0; + } + else + return -1; +} + + +static int cancel( pj_timer_heap_t *ht, + pj_timer_entry *entry, + int dont_call) +{ + long timer_node_slot; + + PJ_CHECK_STACK(); + + // Check to see if the timer_id is out of range + if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size) + return 0; + + timer_node_slot = ht->timer_ids[entry->_timer_id]; + + if (timer_node_slot < 0) // Check to see if timer_id is still valid. + return 0; + + if (entry != ht->heap[timer_node_slot]) + { + pj_assert(entry == ht->heap[timer_node_slot]); + return 0; + } + else + { + remove_node( ht, timer_node_slot); + + if (dont_call == 0) + // Call the close hook. + (*ht->callback)(ht, entry); + return 1; + } +} + + +/* + * Calculate memory size required to create a timer heap. + */ +PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count) +{ + return /* size of the timer heap itself: */ + sizeof(pj_timer_heap_t) + + /* size of each entry: */ + (count+2) * (sizeof(pj_timer_entry*)+sizeof(pj_timer_id_t)) + + /* lock, pool etc: */ + 132; +} + +/* + * Create a new timer heap. + */ +PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool, + pj_size_t size, + pj_timer_heap_t **p_heap) +{ + pj_timer_heap_t *ht; + pj_size_t i; + + PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL); + + *p_heap = NULL; + + /* Magic? */ + size += 2; + + /* Allocate timer heap data structure from the pool */ + ht = PJ_POOL_ALLOC_T(pool, pj_timer_heap_t); + if (!ht) + return PJ_ENOMEM; + + /* Initialize timer heap sizes */ + ht->max_size = size; + ht->cur_size = 0; + ht->max_entries_per_poll = DEFAULT_MAX_TIMED_OUT_PER_POLL; + ht->timer_ids_freelist = 1; + ht->pool = pool; + + /* Lock. */ + ht->lock = NULL; + ht->auto_delete_lock = 0; + + // Create the heap array. + ht->heap = (pj_timer_entry**) + pj_pool_alloc(pool, sizeof(pj_timer_entry*) * size); + if (!ht->heap) + return PJ_ENOMEM; + + // Create the parallel + ht->timer_ids = (pj_timer_id_t *) + pj_pool_alloc( pool, sizeof(pj_timer_id_t) * size); + if (!ht->timer_ids) + return PJ_ENOMEM; + + // Initialize the "freelist," which uses negative values to + // distinguish freelist elements from "pointers" into the + // array. + for (i=0; itimer_ids[i] = -((pj_timer_id_t) (i + 1)); + + *p_heap = ht; + return PJ_SUCCESS; +} + +PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *ht ) +{ + if (ht->lock && ht->auto_delete_lock) { + pj_lock_destroy(ht->lock); + ht->lock = NULL; + } +} + +PJ_DEF(void) pj_timer_heap_set_lock( pj_timer_heap_t *ht, + pj_lock_t *lock, + pj_bool_t auto_del ) +{ + if (ht->lock && ht->auto_delete_lock) + pj_lock_destroy(ht->lock); + + ht->lock = lock; + ht->auto_delete_lock = auto_del; +} + + +PJ_DEF(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht, + unsigned count ) +{ + unsigned old_count = ht->max_entries_per_poll; + ht->max_entries_per_poll = count; + return old_count; +} + +PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry, + int id, + void *user_data, + pj_timer_heap_callback *cb ) +{ + pj_assert(entry && cb); + + entry->_timer_id = -1; + entry->id = id; + entry->user_data = user_data; + entry->cb = cb; + + return entry; +} + +#if PJ_TIMER_DEBUG +PJ_DEF(pj_status_t) pj_timer_heap_schedule_dbg( pj_timer_heap_t *ht, + pj_timer_entry *entry, + const pj_time_val *delay, + const char *src_file, + int src_line) +#else +PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht, + pj_timer_entry *entry, + const pj_time_val *delay) +#endif +{ + pj_status_t status; + pj_time_val expires; + + PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL); + PJ_ASSERT_RETURN(entry->cb != NULL, PJ_EINVAL); + + /* Prevent same entry from being scheduled more than once */ + PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP); + +#if PJ_TIMER_DEBUG + entry->src_file = src_file; + entry->src_line = src_line; +#endif + pj_gettickcount(&expires); + PJ_TIME_VAL_ADD(expires, *delay); + + lock_timer_heap(ht); + status = schedule_entry(ht, entry, &expires); + unlock_timer_heap(ht); + + return status; +} + +PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht, + pj_timer_entry *entry) +{ + int count; + + PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL); + + lock_timer_heap(ht); + count = cancel(ht, entry, 1); + unlock_timer_heap(ht); + + return count; +} + +PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht, + pj_time_val *next_delay ) +{ + pj_time_val now; + unsigned count; + + PJ_ASSERT_RETURN(ht, 0); + + if (!ht->cur_size && next_delay) { + next_delay->sec = next_delay->msec = PJ_MAXINT32; + return 0; + } + + count = 0; + pj_gettickcount(&now); + + lock_timer_heap(ht); + while ( ht->cur_size && + PJ_TIME_VAL_LTE(ht->heap[0]->_timer_value, now) && + count < ht->max_entries_per_poll ) + { + pj_timer_entry *node = remove_node(ht, 0); + ++count; + + unlock_timer_heap(ht); + if (node->cb) + (*node->cb)(ht, node); + lock_timer_heap(ht); + } + if (ht->cur_size && next_delay) { + *next_delay = ht->heap[0]->_timer_value; + PJ_TIME_VAL_SUB(*next_delay, now); + if (next_delay->sec < 0 || next_delay->msec < 0) + next_delay->sec = next_delay->msec = 0; + } else if (next_delay) { + next_delay->sec = next_delay->msec = PJ_MAXINT32; + } + unlock_timer_heap(ht); + + return count; +} + +PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht ) +{ + PJ_ASSERT_RETURN(ht, 0); + + return ht->cur_size; +} + +PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht, + pj_time_val *timeval) +{ + pj_assert(ht->cur_size != 0); + if (ht->cur_size == 0) + return PJ_ENOTFOUND; + + lock_timer_heap(ht); + *timeval = ht->heap[0]->_timer_value; + unlock_timer_heap(ht); + + return PJ_SUCCESS; +} + +#if PJ_TIMER_DEBUG +PJ_DEF(void) pj_timer_heap_dump(pj_timer_heap_t *ht) +{ + lock_timer_heap(ht); + + PJ_LOG(3,(THIS_FILE, "Dumping timer heap:")); + PJ_LOG(3,(THIS_FILE, " Cur size: %d entries, max: %d", + (int)ht->cur_size, (int)ht->max_size)); + + if (ht->cur_size) { + unsigned i; + pj_time_val now; + + PJ_LOG(3,(THIS_FILE, " Entries: ")); + PJ_LOG(3,(THIS_FILE, " _id\tId\tElapsed\tSource")); + PJ_LOG(3,(THIS_FILE, " ----------------------------------")); + + pj_gettickcount(&now); + + for (i=0; i<(unsigned)ht->cur_size; ++i) { + pj_timer_entry *e = ht->heap[i]; + pj_time_val delta; + + if (PJ_TIME_VAL_LTE(e->_timer_value, now)) + delta.sec = delta.msec = 0; + else { + delta = e->_timer_value; + PJ_TIME_VAL_SUB(delta, now); + } + + PJ_LOG(3,(THIS_FILE, " %d\t%d\t%d.%03d\t%s:%d", + e->_timer_id, e->id, + (int)delta.sec, (int)delta.msec, + e->src_file, e->src_line)); + } + } + + unlock_timer_heap(ht); +} +#endif + diff --git a/pjlib/src/pj/timer_symbian.cpp b/pjlib/src/pj/timer_symbian.cpp new file mode 100644 index 0000000..47aa984 --- /dev/null +++ b/pjlib/src/pj/timer_symbian.cpp @@ -0,0 +1,446 @@ +/* $Id: timer_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include +#include + +#include "os_symbian.h" + + +#define DEFAULT_MAX_TIMED_OUT_PER_POLL (64) + +// Maximum number of miliseconds that RTimer.At() supports +#define MAX_RTIMER_INTERVAL 2147 + +/* Absolute maximum number of timer entries */ +#ifndef PJ_SYMBIAN_TIMER_MAX_COUNT +# define PJ_SYMBIAN_TIMER_MAX_COUNT 65535 +#endif + +/* Get the number of free slots in the timer heap */ +#define FREECNT(th) (th->max_size - th->cur_size) + +// Forward declaration +class CPjTimerEntry; + +/** + * The implementation of timer heap. + */ +struct pj_timer_heap_t +{ + /** Maximum size of the heap. */ + pj_size_t max_size; + + /** Current size of the heap. */ + pj_size_t cur_size; + + /** Array of timer entries. A scheduled timer will occupy one slot, and + * the slot number will be saved in entry->_timer_id + */ + CPjTimerEntry **entries; + + /** Array of free slot indexes in the "entries" array */ + int *free_slots; +}; + +/** + * Active object for each timer entry. + */ +class CPjTimerEntry : public CActive +{ +public: + pj_timer_entry *entry_; + + static CPjTimerEntry* NewL( pj_timer_heap_t *timer_heap, + pj_timer_entry *entry, + const pj_time_val *delay); + + ~CPjTimerEntry(); + + virtual void RunL(); + virtual void DoCancel(); + +private: + pj_timer_heap_t *timer_heap_; + RTimer rtimer_; + pj_uint32_t interval_left_; + + CPjTimerEntry(pj_timer_heap_t *timer_heap, pj_timer_entry *entry); + void ConstructL(const pj_time_val *delay); + void Schedule(); +}; + +////////////////////////////////////////////////////////////////////////////// +/* + * Implementation. + */ + +/* Grow timer heap to the specified size */ +static pj_status_t realloc_timer_heap(pj_timer_heap_t *th, pj_size_t new_size) +{ + typedef CPjTimerEntry *entry_ptr; + CPjTimerEntry **entries = NULL; + int *free_slots = NULL; + unsigned i, j; + + if (new_size > PJ_SYMBIAN_TIMER_MAX_COUNT) { + /* Just some sanity limit */ + new_size = PJ_SYMBIAN_TIMER_MAX_COUNT; + if (new_size <= th->max_size) { + /* We've grown large enough */ + pj_assert(!"Too many timer heap entries"); + return PJ_ETOOMANY; + } + } + + /* Allocate entries, move entries from the old array if there is one */ + entries = new entry_ptr[new_size]; + if (th->entries) { + pj_memcpy(entries, th->entries, th->max_size * sizeof(th->entries[0])); + } + /* Initialize the remaining new area */ + pj_bzero(&entries[th->max_size], + (new_size - th->max_size) * sizeof(th->entries[0])); + + /* Allocate free slots array */ + free_slots = new int[new_size]; + if (th->free_slots) { + pj_memcpy(free_slots, th->free_slots, + FREECNT(th) * sizeof(th->free_slots[0])); + } + /* Initialize the remaining new area */ + for (i=FREECNT(th), j=th->max_size; jentries; + th->entries = entries; + th->max_size = new_size; + delete [] th->free_slots; + th->free_slots = free_slots; + + return PJ_SUCCESS; +} + +/* Allocate and register an entry to timer heap for newly scheduled entry */ +static pj_status_t add_entry(pj_timer_heap_t *th, CPjTimerEntry *entry) +{ + pj_status_t status; + int slot; + + /* Check that there's still capacity left in the timer heap */ + if (FREECNT(th) < 1) { + // Grow the timer heap twice the capacity + status = realloc_timer_heap(th, th->max_size * 2); + if (status != PJ_SUCCESS) + return status; + } + + /* Allocate one free slot. Use LIFO */ + slot = th->free_slots[FREECNT(th)-1]; + PJ_ASSERT_RETURN((slot >= 0) && (slot < (int)th->max_size) && + (th->entries[slot]==NULL), PJ_EBUG); + + th->free_slots[FREECNT(th)-1] = -1; + th->entries[slot] = entry; + entry->entry_->_timer_id = slot; + ++th->cur_size; + + return PJ_SUCCESS; +} + +/* Free a slot when an entry's timer has elapsed or cancel */ +static pj_status_t remove_entry(pj_timer_heap_t *th, CPjTimerEntry *entry) +{ + int slot = entry->entry_->_timer_id; + + PJ_ASSERT_RETURN(slot >= 0 && slot < (int)th->max_size, PJ_EBUG); + PJ_ASSERT_RETURN(FREECNT(th) < th->max_size, PJ_EBUG); + PJ_ASSERT_RETURN(th->entries[slot]==entry, PJ_EBUG); + PJ_ASSERT_RETURN(th->free_slots[FREECNT(th)]==-1, PJ_EBUG); + + th->entries[slot] = NULL; + th->free_slots[FREECNT(th)] = slot; + entry->entry_->_timer_id = -1; + --th->cur_size; + + return PJ_SUCCESS; +} + + +CPjTimerEntry::CPjTimerEntry(pj_timer_heap_t *timer_heap, + pj_timer_entry *entry) +: CActive(PJ_SYMBIAN_TIMER_PRIORITY), entry_(entry), timer_heap_(timer_heap), + interval_left_(0) +{ +} + +CPjTimerEntry::~CPjTimerEntry() +{ + Cancel(); + rtimer_.Close(); +} + +void CPjTimerEntry::Schedule() +{ + pj_int32_t interval; + + if (interval_left_ > MAX_RTIMER_INTERVAL) { + interval = MAX_RTIMER_INTERVAL; + } else { + interval = interval_left_; + } + + interval_left_ -= interval; + rtimer_.After(iStatus, interval * 1000); + SetActive(); +} + +void CPjTimerEntry::ConstructL(const pj_time_val *delay) +{ + rtimer_.CreateLocal(); + CActiveScheduler::Add(this); + + interval_left_ = PJ_TIME_VAL_MSEC(*delay); + Schedule(); +} + +CPjTimerEntry* CPjTimerEntry::NewL(pj_timer_heap_t *timer_heap, + pj_timer_entry *entry, + const pj_time_val *delay) +{ + CPjTimerEntry *self = new CPjTimerEntry(timer_heap, entry); + CleanupStack::PushL(self); + self->ConstructL(delay); + CleanupStack::Pop(self); + + return self; +} + +void CPjTimerEntry::RunL() +{ + if (interval_left_ > 0) { + Schedule(); + return; + } + + remove_entry(timer_heap_, this); + entry_->cb(timer_heap_, entry_); + + // Finger's crossed! + delete this; +} + +void CPjTimerEntry::DoCancel() +{ + /* It's possible that _timer_id is -1, see schedule(). In this case, + * the entry has not been added to the timer heap, so don't remove + * it. + */ + if (entry_ && entry_->_timer_id != -1) + remove_entry(timer_heap_, this); + + rtimer_.Cancel(); +} + + +////////////////////////////////////////////////////////////////////////////// + + +/* + * Calculate memory size required to create a timer heap. + */ +PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count) +{ + return /* size of the timer heap itself: */ + sizeof(pj_timer_heap_t) + + /* size of each entry: */ + (count+2) * (sizeof(void*)+sizeof(int)) + + /* lock, pool etc: */ + 132; +} + +/* + * Create a new timer heap. + */ +PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool, + pj_size_t size, + pj_timer_heap_t **p_heap) +{ + pj_timer_heap_t *ht; + pj_status_t status; + + PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL); + + *p_heap = NULL; + + /* Allocate timer heap data structure from the pool */ + ht = PJ_POOL_ZALLOC_T(pool, pj_timer_heap_t); + if (!ht) + return PJ_ENOMEM; + + /* Allocate slots */ + status = realloc_timer_heap(ht, size); + if (status != PJ_SUCCESS) + return status; + + *p_heap = ht; + return PJ_SUCCESS; +} + +PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *ht ) +{ + /* Cancel and delete pending active objects */ + if (ht->entries) { + unsigned i; + for (i=0; imax_size; ++i) { + if (ht->entries[i]) { + ht->entries[i]->entry_ = NULL; + ht->entries[i]->Cancel(); + delete ht->entries[i]; + ht->entries[i] = NULL; + } + } + } + + delete [] ht->entries; + delete [] ht->free_slots; + + ht->entries = NULL; + ht->free_slots = NULL; +} + +PJ_DEF(void) pj_timer_heap_set_lock( pj_timer_heap_t *ht, + pj_lock_t *lock, + pj_bool_t auto_del ) +{ + PJ_UNUSED_ARG(ht); + if (auto_del) + pj_lock_destroy(lock); +} + + +PJ_DEF(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht, + unsigned count ) +{ + /* Not applicable */ + PJ_UNUSED_ARG(count); + return ht->max_size; +} + +PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry, + int id, + void *user_data, + pj_timer_heap_callback *cb ) +{ + pj_assert(entry && cb); + + entry->_timer_id = -1; + entry->id = id; + entry->user_data = user_data; + entry->cb = cb; + + return entry; +} + +PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht, + pj_timer_entry *entry, + const pj_time_val *delay) +{ + CPjTimerEntry *timerObj; + pj_status_t status; + + PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL); + PJ_ASSERT_RETURN(entry->cb != NULL, PJ_EINVAL); + + /* Prevent same entry from being scheduled more than once */ + PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP); + + entry->_timer_id = -1; + + timerObj = CPjTimerEntry::NewL(ht, entry, delay); + status = add_entry(ht, timerObj); + if (status != PJ_SUCCESS) { + timerObj->Cancel(); + delete timerObj; + return status; + } + + return PJ_SUCCESS; +} + +PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht, + pj_timer_entry *entry) +{ + PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL); + + if (entry->_timer_id >= 0 && entry->_timer_id < (int)ht->max_size) { + CPjTimerEntry *timerObj = ht->entries[entry->_timer_id]; + if (timerObj) { + timerObj->Cancel(); + delete timerObj; + return 1; + } else { + return 0; + } + } else { + return 0; + } +} + +PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht, + pj_time_val *next_delay ) +{ + /* Polling is not necessary on Symbian, since all async activities + * are registered to active scheduler. + */ + PJ_UNUSED_ARG(ht); + if (next_delay) { + next_delay->sec = 1; + next_delay->msec = 0; + } + return 0; +} + +PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht ) +{ + PJ_ASSERT_RETURN(ht, 0); + + return ht->cur_size; +} + +PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht, + pj_time_val *timeval) +{ + /* We don't support this! */ + PJ_UNUSED_ARG(ht); + + timeval->sec = 1; + timeval->msec = 0; + + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/types.c b/pjlib/src/pj/types.c new file mode 100644 index 0000000..212afcf --- /dev/null +++ b/pjlib/src/pj/types.c @@ -0,0 +1,46 @@ +/* $Id: types.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include + +PJ_DEF(void) pj_time_val_normalize(pj_time_val *t) +{ + PJ_CHECK_STACK(); + + if (t->msec >= 1000) { + t->sec += (t->msec / 1000); + t->msec = (t->msec % 1000); + } + else if (t->msec <= -1000) { + do { + t->sec--; + t->msec += 1000; + } while (t->msec <= -1000); + } + + if (t->sec >= 1 && t->msec < 0) { + t->sec--; + t->msec += 1000; + + } else if (t->sec < 0 && t->msec > 0) { + t->sec++; + t->msec -= 1000; + } +} diff --git a/pjlib/src/pj/unicode_symbian.cpp b/pjlib/src/pj/unicode_symbian.cpp new file mode 100644 index 0000000..be109e7 --- /dev/null +++ b/pjlib/src/pj/unicode_symbian.cpp @@ -0,0 +1,76 @@ +/* $Id: unicode_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + +#include "os_symbian.h" + + +/* + * Convert ANSI strings to Unicode strings. + */ +PJ_DEF(wchar_t*) pj_ansi_to_unicode( const char *str, pj_size_t len, + wchar_t *wbuf, pj_size_t wbuf_count) +{ + TPtrC8 aForeign((const TUint8*)str, (TInt)len); + TPtr16 aUnicode((TUint16*)wbuf, (TInt)(wbuf_count-1)); + TInt left; + + left = PjSymbianOS::Instance()->ConvertToUnicode(aUnicode, aForeign); + + if (left != 0) { + // Error, or there are unconvertable characters + *wbuf = 0; + } else { + if (len < wbuf_count) + wbuf[len] = 0; + else + wbuf[len-1] = 0; + } + + return wbuf; +} + + +/* + * Convert Unicode string to ANSI string. + */ +PJ_DEF(char*) pj_unicode_to_ansi( const wchar_t *wstr, pj_size_t len, + char *buf, pj_size_t buf_size) +{ + TPtrC16 aUnicode((const TUint16*)wstr, (TInt)len); + TPtr8 aForeign((TUint8*)buf, (TInt)(buf_size-1)); + TInt left; + + left = PjSymbianOS::Instance()->ConvertFromUnicode(aForeign, aUnicode); + + if (left != 0) { + // Error, or there are unconvertable characters + buf[0] = '\0'; + } else { + if (len < buf_size) + buf[len] = '\0'; + else + buf[len-1] = '\0'; + } + + return buf; +} + + diff --git a/pjlib/src/pj/unicode_win32.c b/pjlib/src/pj/unicode_win32.c new file mode 100644 index 0000000..68557ba --- /dev/null +++ b/pjlib/src/pj/unicode_win32.c @@ -0,0 +1,59 @@ +/* $Id: unicode_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 +#include +#include +#include + + +PJ_DEF(wchar_t*) pj_ansi_to_unicode(const char *s, pj_size_t len, + wchar_t *buf, pj_size_t buf_count) +{ + PJ_ASSERT_RETURN(s && buf, NULL); + + len = MultiByteToWideChar(CP_ACP, 0, s, len, + buf, buf_count); + if (buf_count) { + if (len < buf_count) + buf[len] = 0; + else + buf[len-1] = 0; + } + + return buf; +} + + +PJ_DEF(char*) pj_unicode_to_ansi( const wchar_t *wstr, pj_size_t len, + char *buf, pj_size_t buf_size) +{ + PJ_ASSERT_RETURN(wstr && buf, NULL); + + len = WideCharToMultiByte(CP_ACP, 0, wstr, len, buf, buf_size, NULL, NULL); + if (buf_size) { + if (len < buf_size) + buf[len] = '\0'; + else + buf[len-1] = '\0'; + } + + return buf; +} + -- cgit v1.2.3