summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2005-12-30 23:50:15 +0000
committerBenny Prijono <bennylp@teluu.com>2005-12-30 23:50:15 +0000
commit944562492d0c16b9e44ec4e1cc97657846d82cd0 (patch)
tree6e6c2e65e95e479384faeeaab4f525fa91cc7f27
parent9b86a2145e18cb843e69167b10f3c7414c11c634 (diff)
Basic module, transport, and sending messages
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@106 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjlib/include/pj/assert.h20
-rw-r--r--pjlib/include/pj/os.h69
-rw-r--r--pjlib/include/pj/sock.h4
-rw-r--r--pjlib/include/pj/types.h3
-rw-r--r--pjlib/src/pj/os_core_win32.c117
-rw-r--r--pjlib/src/pj/sock_bsd.c5
-rw-r--r--pjsip/build/pjsip_core.dsp6
-rw-r--r--pjsip/build/test_pjsip.dsp12
-rw-r--r--pjsip/include/pjsip/sip_endpoint.h94
-rw-r--r--pjsip/include/pjsip/sip_errno.h42
-rw-r--r--pjsip/include/pjsip/sip_module.h88
-rw-r--r--pjsip/include/pjsip/sip_msg.h50
-rw-r--r--pjsip/include/pjsip/sip_transport.h22
-rw-r--r--pjsip/include/pjsip/sip_transport_udp.h2
-rw-r--r--pjsip/include/pjsip/sip_util.h257
-rw-r--r--pjsip/include/pjsip_core.h1
-rw-r--r--pjsip/src/pjsip/sip_endpoint.c645
-rw-r--r--pjsip/src/pjsip/sip_errno.c6
-rw-r--r--pjsip/src/pjsip/sip_msg.c64
-rw-r--r--pjsip/src/pjsip/sip_parser.c2
-rw-r--r--pjsip/src/pjsip/sip_transaction.c563
-rw-r--r--pjsip/src/pjsip/sip_transport.c50
-rw-r--r--pjsip/src/pjsip/sip_transport_udp.c34
-rw-r--r--pjsip/src/pjsip/sip_util.c950
-rw-r--r--pjsip/src/pjsip/sip_util_statefull.c108
-rw-r--r--pjsip/src/test-pjsip/main.c13
-rw-r--r--pjsip/src/test-pjsip/msg_test.c2
-rw-r--r--pjsip/src/test-pjsip/test.c2
-rw-r--r--pjsip/src/test-pjsip/test.h21
-rw-r--r--pjsip/src/test-pjsip/transport_test.c561
-rw-r--r--pjsip/src/test-pjsip/transport_udp_test.c98
-rw-r--r--pjsip/src/test-pjsip/txdata_test.c320
-rw-r--r--pjsip/src/test-pjsip/uri_test.c2
33 files changed, 3189 insertions, 1044 deletions
diff --git a/pjlib/include/pj/assert.h b/pjlib/include/pj/assert.h
index 5a842477..6814e4cd 100644
--- a/pjlib/include/pj/assert.h
+++ b/pjlib/include/pj/assert.h
@@ -67,6 +67,26 @@
# define PJ_ASSERT_RETURN(expr,retval) pj_assert(expr)
#endif
+/**
+ * @hideinitializer
+ * If #PJ_ENABLE_EXTRA_CHECK is declared and non-zero, then
+ * #PJ_ASSERT_ON_FAIL macro will evaluate the expression in @a expr during
+ * run-time. If the expression yields false, assertion will be triggered
+ * and @a exec_on_fail will be executed.
+ *
+ * If #PJ_ENABLE_EXTRA_CHECK is not declared or is zero, then no run-time
+ * checking will be performed. The macro simply evaluates to pj_assert(expr).
+ */
+#if defined(PJ_ENABLE_EXTRA_CHECK) && PJ_ENABLE_EXTRA_CHECK != 0
+# define PJ_ASSERT_ON_FAIL(expr,exec_on_fail) \
+ do { \
+ pj_assert(expr); \
+ if (!(expr)) exec_on_fail; \
+ } while (0)
+#else
+# define PJ_ASSERT_ON_FAIL(expr,exec_on_fail) pj_assert(expr)
+#endif
+
/** @} */
#endif /* __PJ_ASSERT_H__ */
diff --git a/pjlib/include/pj/os.h b/pjlib/include/pj/os.h
index 2917a2ab..8c04364d 100644
--- a/pjlib/include/pj/os.h
+++ b/pjlib/include/pj/os.h
@@ -507,6 +507,75 @@ PJ_DECL(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex);
///////////////////////////////////////////////////////////////////////////////
/**
+ * @defgroup PJ_RW_MUTEX Reader/Writer Mutex
+ * @ingroup PJ_OS
+ * @{
+ * Reader/writer mutex is a classic synchronization object where multiple
+ * readers can acquire the mutex, but only a single writer can acquire the
+ * mutex.
+ */
+typedef struct pj_rwmutex_t pj_rwmutex_t;
+
+/**
+ * Create reader/writer mutex.
+ *
+ * @param pool Pool to allocate memory for the mutex.
+ * @param name Name to be assigned to the mutex.
+ * @param mutex Pointer to receive the newly created mutex.
+ *
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_create(pj_pool_t *pool, const char *name,
+ pj_rwmutex_t **mutex);
+
+/**
+ * Lock the mutex for reading.
+ *
+ * @param mutex The mutex.
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_lock_read(pj_rwmutex_t *mutex);
+
+/**
+ * Lock the mutex for writing.
+ *
+ * @param mutex The mutex.
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_lock_write(pj_rwmutex_t *mutex);
+
+/**
+ * Release read lock.
+ *
+ * @param mutex The mutex.
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_unlock_read(pj_rwmutex_t *mutex);
+
+/**
+ * Release write lock.
+ *
+ * @param mutex The mutex.
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_unlock_write(pj_rwmutex_t *mutex);
+
+/**
+ * Destroy reader/writer mutex.
+ *
+ * @param mutex The mutex.
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_destroy(pj_rwmutex_t *mutex);
+
+
+/**
+ * @}
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
* @defgroup PJ_CRIT_SEC Critical sections.
* @ingroup PJ_OS
* @{
diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h
index 02d88c1e..89883495 100644
--- a/pjlib/include/pj/sock.h
+++ b/pjlib/include/pj/sock.h
@@ -176,13 +176,13 @@ typedef struct pj_in_addr
/**
* This structure describes Internet socket address.
*/
-typedef struct pj_sockaddr_in
+struct pj_sockaddr_in
{
pj_uint16_t sin_family; /**< Address family. */
pj_uint16_t sin_port; /**< Transport layer port number. */
pj_in_addr sin_addr; /**< IP address. */
char sin_zero[8]; /**< Padding. */
-} pj_sockaddr_in;
+};
/**
diff --git a/pjlib/include/pj/types.h b/pjlib/include/pj/types.h
index 62e2c0f4..0099255f 100644
--- a/pjlib/include/pj/types.h
+++ b/pjlib/include/pj/types.h
@@ -218,6 +218,9 @@ typedef long pj_sock_t;
/** Generic socket address. */
typedef void pj_sockaddr_t;
+/** Forward declaration. */
+typedef struct pj_sockaddr_in pj_sockaddr_in;
+
/** Color type. */
typedef unsigned int pj_color_t;
diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c
index 756b88a8..eac778f6 100644
--- a/pjlib/src/pj/os_core_win32.c
+++ b/pjlib/src/pj/os_core_win32.c
@@ -859,6 +859,123 @@ PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
#endif
///////////////////////////////////////////////////////////////////////////////
+
+struct pj_rwmutex_t
+{
+ pj_mutex_t *read_lock, *write_lock;
+ int 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(pool, sizeof(struct pj_rwmutex_t));
+
+ status = pj_mutex_create_simple(pool, name, &rwmutex ->read_lock);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = pj_mutex_create_recursive(pool, name, &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)
+ return status;
+
+ mutex->reader_count++;
+ if (mutex->reader_count == 1)
+ pj_mutex_lock(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_mutex_lock(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)
+ return status;
+
+ pj_assert(mutex->reader_count >= 1);
+
+ --mutex->reader_count;
+ if (mutex->reader_count == 0)
+ pj_mutex_unlock(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);
+ return pj_mutex_unlock(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_mutex_destroy(mutex->write_lock);
+ return PJ_SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
/*
* pj_enter_critical_section()
*/
diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c
index 5a096936..3ded3a54 100644
--- a/pjlib/src/pj/sock_bsd.c
+++ b/pjlib/src/pj/sock_bsd.c
@@ -187,7 +187,7 @@ PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr,
{
PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(str_addr && str_addr->slen < PJ_MAX_HOSTNAME,
+ PJ_ASSERT_RETURN(!str_addr || str_addr->slen < PJ_MAX_HOSTNAME,
(addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
addr->sin_family = AF_INET;
@@ -224,8 +224,7 @@ 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 && str_addr,
- (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
+ PJ_ASSERT_RETURN(addr, (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
addr->sin_family = PJ_AF_INET;
pj_sockaddr_in_set_port(addr, port);
diff --git a/pjsip/build/pjsip_core.dsp b/pjsip/build/pjsip_core.dsp
index 8893fa00..264799c7 100644
--- a/pjsip/build/pjsip_core.dsp
+++ b/pjsip/build/pjsip_core.dsp
@@ -124,6 +124,7 @@ SOURCE=..\src\pjsip\sip_tel_uri.c
# Begin Source File
SOURCE=..\src\pjsip\sip_transaction.c
+# PROP Exclude_From_Build 1
# End Source File
# Begin Source File
@@ -141,6 +142,11 @@ SOURCE=..\src\pjsip\sip_uri.c
SOURCE=..\src\pjsip\sip_util.c
# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_util_statefull.c
+# PROP Exclude_From_Build 1
+# End Source File
# End Group
# Begin Group "Header Files"
diff --git a/pjsip/build/test_pjsip.dsp b/pjsip/build/test_pjsip.dsp
index 2ca46668..1c8ad9f2 100644
--- a/pjsip/build/test_pjsip.dsp
+++ b/pjsip/build/test_pjsip.dsp
@@ -101,6 +101,18 @@ SOURCE="..\src\test-pjsip\test.c"
# End Source File
# Begin Source File
+SOURCE="..\src\test-pjsip\transport_test.c"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\test-pjsip\transport_udp_test.c"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\test-pjsip\txdata_test.c"
+# End Source File
+# Begin Source File
+
SOURCE="..\src\test-pjsip\uri_test.c"
# End Source File
# End Group
diff --git a/pjsip/include/pjsip/sip_endpoint.h b/pjsip/include/pjsip/sip_endpoint.h
index 8254d508..d9d03a97 100644
--- a/pjsip/include/pjsip/sip_endpoint.h
+++ b/pjsip/include/pjsip/sip_endpoint.h
@@ -115,6 +115,34 @@ PJ_DECL(const pj_str_t*) pjsip_endpt_name(const pjsip_endpoint *endpt);
PJ_DECL(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt,
const pj_time_val *max_timeout);
+
+/**
+ * Schedule timer to endpoint's timer heap. Application must poll the endpoint
+ * periodically (by calling #pjsip_endpt_handle_events) to ensure that the
+ * timer events are handled in timely manner. When the timeout for the timer
+ * has elapsed, the callback specified in the entry argument will be called.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt The endpoint.
+ * @param entry The timer entry.
+ * @param delay The relative delay of the timer.
+ * @return PJ_OK (zero) if successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
+ pj_timer_entry *entry,
+ const pj_time_val *delay );
+
+/**
+ * Cancel the previously registered timer.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt The endpoint.
+ * @param entry The timer entry previously registered.
+ */
+PJ_DECL(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt,
+ pj_timer_entry *entry );
+
+
/**
* Dump endpoint status to the log. This will print the status to the log
* with log level 3.
@@ -126,6 +154,35 @@ PJ_DECL(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt,
*/
PJ_DECL(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail );
+
+/**
+ * Register new module to the endpoint.
+ * The endpoint will then call the load and start function in the module to
+ * properly initialize the module, and assign a unique module ID for the
+ * module.
+ *
+ * @param endpt The endpoint.
+ * @param module The module to be registered.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_register_module( pjsip_endpoint *endpt,
+ pjsip_module *module );
+
+/**
+ * Unregister a module from the endpoint.
+ * The endpoint will then call the stop and unload function in the module to
+ * properly shutdown the module.
+ *
+ * @param endpt The endpoint.
+ * @param module The module to be registered.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_unregister_module( pjsip_endpoint *endpt,
+ pjsip_module *module );
+
+
/**
* Create pool from the endpoint. All SIP components should allocate their
* memory pool by calling this function, to make sure that the pools are
@@ -156,32 +213,6 @@ PJ_DECL(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt,
pj_pool_t *pool );
/**
- * Schedule timer to endpoint's timer heap. Application must poll the endpoint
- * periodically (by calling #pjsip_endpt_handle_events) to ensure that the
- * timer events are handled in timely manner. When the timeout for the timer
- * has elapsed, the callback specified in the entry argument will be called.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The endpoint.
- * @param entry The timer entry.
- * @param delay The relative delay of the timer.
- * @return PJ_OK (zero) if successfull.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
- pj_timer_entry *entry,
- const pj_time_val *delay );
-
-/**
- * Cancel the previously registered timer.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The endpoint.
- * @param entry The timer entry previously registered.
- */
-PJ_DECL(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt,
- pj_timer_entry *entry );
-
-/**
* Create a new transaction. After creating the transaction, application MUST
* initialize the transaction as either UAC or UAS (by calling
* #pjsip_tsx_init_uac or #pjsip_tsx_init_uas), then must register the
@@ -278,11 +309,12 @@ PJ_DECL(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt);
*
* @see pjsip_transport_get
*/
-PJ_DECL(pj_status_t) pjsip_endpt_alloc_transport( pjsip_endpoint *endpt,
- pjsip_transport_type_e type,
- const pj_sockaddr *remote,
- int addr_len,
- pjsip_transport **p_transport);
+PJ_DECL(pj_status_t)
+pjsip_endpt_acquire_transport( pjsip_endpoint *endpt,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_t *remote,
+ int addr_len,
+ pjsip_transport **p_transport);
/**
* Get additional headers to be put in outgoing request message.
diff --git a/pjsip/include/pjsip/sip_errno.h b/pjsip/include/pjsip/sip_errno.h
index eefda673..4d50c75d 100644
--- a/pjsip/include/pjsip/sip_errno.h
+++ b/pjsip/include/pjsip/sip_errno.h
@@ -93,7 +93,7 @@ PJ_DECL(pj_str_t) pjsip_strerror( pj_status_t status, char *buffer,
***********************************************************/
/**
* @hideinitializer
- * Invalid message (syntax error)
+ * General invalid message error (e.g. syntax error)
*/
#define PJSIP_EINVALIDMSG (PJSIP_ERRNO_START_PJSIP + 20) /* 171020 */
/**
@@ -113,19 +113,45 @@ PJ_DECL(pj_str_t) pjsip_strerror( pj_status_t status, char *buffer,
#define PJSIP_EPARTIALMSG (PJSIP_ERRNO_START_PJSIP + 23) /* 171023 */
/**
* @hideinitializer
+ * Missing Request-URI.
+ */
+#define PJSIP_EMISSINGREQURI (PJSIP_ERRNO_START_PJSIP + 24) /* 171024 */
+/**
+ * @hideinitializer
* Missing required header(s).
*/
-#define PJSIP_EMISSINGHDR (PJSIP_ERRNO_START_PJSIP + 24) /* 171024 */
+#define PJSIP_EMISSINGHDR (PJSIP_ERRNO_START_PJSIP + 25) /* 171025 */
+/**
+ * @hideinitializer
+ * Missing message body.
+ */
+#define PJSIP_EMISSINGBODY (PJSIP_ERRNO_START_PJSIP + 26) /* 171026 */
/**
* @hideinitializer
* Invalid Via header in response (sent-by, etc).
*/
-#define PJSIP_EINVALIDVIA (PJSIP_ERRNO_START_PJSIP + 25) /* 171025 */
+#define PJSIP_EINVALIDVIA (PJSIP_ERRNO_START_PJSIP + 27) /* 171027 */
/**
* @hideinitializer
* Multiple Via headers in response.
*/
-#define PJSIP_EMULTIPLEVIA (PJSIP_ERRNO_START_PJSIP + 26) /* 171026 */
+#define PJSIP_EMULTIPLEVIA (PJSIP_ERRNO_START_PJSIP + 28) /* 171028 */
+/**
+ * @hideinitializer
+ * Invalid request URI.
+ */
+#define PJSIP_EINVALIDREQURI (PJSIP_ERRNO_START_PJSIP + 29) /* 171029 */
+/**
+ * @hideinitializer
+ * Expecting request message.
+ */
+#define PJSIP_ENOTREQUESTMSG (PJSIP_ERRNO_START_PJSIP + 30) /* 171030 */
+/**
+ * @hideinitializer
+ * Expecting response message.
+ */
+#define PJSIP_ENOTRESPONSEMSG (PJSIP_ERRNO_START_PJSIP + 31) /* 171031 */
+
/************************************************************
* TRANSPORT ERRORS
@@ -144,7 +170,13 @@ PJ_DECL(pj_str_t) pjsip_strerror( pj_status_t status, char *buffer,
* @hideinitializer
* Rx buffer overflow. See also PJSIP_EMSGTOOLONG.
*/
-#define PJSIP_ERXOVERFLOW (PJSIP_ERRNO_START_PJSIP + 42)/* 171042 */
+#define PJSIP_ERXOVERFLOW (PJSIP_ERRNO_START_PJSIP + 42) /* 171042 */
+/**
+ * @hideinitializer
+ * This is not really an error, it just informs application that
+ * transmit data has been deleted on return of pjsip_tx_data_dec_ref().
+ */
+#define PJSIP_EBUFDESTROYED (PJSIP_ERRNO_START_PJSIP + 43) /* 171043 */
/************************************************************
diff --git a/pjsip/include/pjsip/sip_module.h b/pjsip/include/pjsip/sip_module.h
index 345be88a..0f188194 100644
--- a/pjsip/include/pjsip/sip_module.h
+++ b/pjsip/include/pjsip/sip_module.h
@@ -41,28 +41,31 @@ PJ_BEGIN_DECL
*/
struct pjsip_module
{
+ /** To allow chaining of modules in the endpoint. */
+ PJ_DECL_LIST_MEMBER(struct pjsip_module);
+
/**
* Module name.
*/
pj_str_t name;
/**
- * Flag to indicate the type of interfaces supported by the module.
+ * Module ID.
*/
- pj_uint32_t flag;
+ int id;
/**
* Integer number to identify module initialization and start order with
* regard to other modules. Higher number will make the module gets
* initialized later.
*/
- pj_uint32_t priority;
+ int priority;
/**
* Opaque data which can be used by a module to identify a resource within
* the module itself.
*/
- void *mod_data;
+ void *user_data;
/**
* Number of methods supported by this module.
@@ -78,22 +81,24 @@ struct pjsip_module
* Pointer to function to be called to initialize the module.
*
* @param endpt The endpoint instance.
- * @param mod The module.
- * @param id The unique module ID assigned to this module.
- *
- * @return Module should return zero when initialization succeed.
+ * @return Module should return PJ_SUCCESS to indicate success.
*/
- pj_status_t (*init_module)(pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id);
+ pj_status_t (*load)(pjsip_endpoint *endpt);
/**
* Pointer to function to be called to start the module.
*
- * @param mod The module.
- *
* @return Module should return zero to indicate success.
*/
- pj_status_t (*start_module)(struct pjsip_module *mod);
+ pj_status_t (*start)(void);
+
+ /**
+ * Pointer to function to be called to deinitialize the module before
+ * it is unloaded.
+ *
+ * @return Module should return PJ_SUCCESS to indicate success.
+ */
+ pj_status_t (*stop)(void);
/**
* Pointer to function to be called to deinitialize the module before
@@ -101,34 +106,55 @@ struct pjsip_module
*
* @param mod The module.
*
- * @return Module should return zero to indicate success.
+ * @return Module should return PJ_SUCCESS to indicate success.
*/
- pj_status_t (*deinit_module)(struct pjsip_module *mod);
+ pj_status_t (*unload)(void);
/**
- * Pointer to function to receive transaction related events.
- * If the module doesn't wish to receive such notification, this member
- * must be set to NULL.
+ * Called to process incoming request.
*
- * @param mod The module.
- * @param event The transaction event.
+ * @param rdata The incoming message.
+ *
+ * @return Module should return PJ_TRUE if it handles the request,
+ * or otherwise it should return PJ_FALSE to allow other
+ * modules to handle the request.
*/
- void (*tsx_handler)(struct pjsip_module *mod, pjsip_event *event);
+ pj_bool_t (*on_rx_request)(pjsip_rx_data *rdata);
+
+ /**
+ * Called to processed incoming response.
+ *
+ * @param rdata The incoming message.
+ *
+ * @return Module should return PJ_TRUE if it handles the
+ * response, or otherwise it should return PJ_FALSE to
+ * allow other modules to handle the response.
+ */
+ pj_bool_t (*on_rx_response)(pjsip_rx_data *rdata);
+
+ /**
+ * Called when this module is acting as transaction user for the specified
+ * transaction, when the transaction's state has changed.
+ *
+ * @param tsx The transaction.
+ * @param event The event which has caused the transaction state
+ * to change.
+ */
+ void (*on_tsx_state)(pjsip_transaction *tsx, pjsip_event *event);
+
};
/**
- * Prototype of function to register static modules (eg modules that are
- * linked staticly with the application). This function must be implemented
- * by any applications that use PJSIP library.
- *
- * @param count [input/output] On input, it contains the maximum number of
- * elements in the array. On output, the function fills with
- * the number of modules to be registered.
- * @param modules [output] array of pointer to modules to be registered.
+ * Module priority guidelines.
*/
-pj_status_t register_static_modules( pj_size_t *count,
- pjsip_module **modules );
+enum pjsip_module_priority
+{
+ PJSIP_MOD_PRIORITY_TSX_LAYER = 4,
+ PJSIP_MOD_PRIORITY_UA_PROXY_LAYER = 16,
+ PJSIP_MOD_PRIORITY_APPLICATION = 32,
+};
+
/**
* @}
diff --git a/pjsip/include/pjsip/sip_msg.h b/pjsip/include/pjsip/sip_msg.h
index 836ce37b..e1866048 100644
--- a/pjsip/include/pjsip/sip_msg.h
+++ b/pjsip/include/pjsip/sip_msg.h
@@ -52,27 +52,14 @@ PJ_BEGIN_DECL
*/
typedef enum pjsip_method_e
{
- /** INVITE method, for establishing dialogs. */
- PJSIP_INVITE_METHOD,
+ PJSIP_INVITE_METHOD, /**< INVITE method, for establishing dialogs. */
+ PJSIP_CANCEL_METHOD, /**< CANCEL method, for cancelling request. */
+ PJSIP_ACK_METHOD, /**< ACK method. */
+ PJSIP_BYE_METHOD, /**< BYE method, for terminating dialog. */
+ PJSIP_REGISTER_METHOD, /**< REGISTER method. */
+ PJSIP_OPTIONS_METHOD, /**< OPTIONS method. */
- /** CANCEL method, for cancelling request. */
- PJSIP_CANCEL_METHOD,
-
- /** ACK method, for acknowledging final response to INVITE. */
- PJSIP_ACK_METHOD,
-
- /** BYE method, for terminating dialog. */
- PJSIP_BYE_METHOD,
-
- /** REGISTER method. */
- PJSIP_REGISTER_METHOD,
-
- /** OPTIONS method, for querying remote capabilities. */
- PJSIP_OPTIONS_METHOD,
-
- /** Other method, which means that the method name itself will be stored
- elsewhere. */
- PJSIP_OTHER_METHOD,
+ PJSIP_OTHER_METHOD, /**< Other method. */
} pjsip_method_e;
@@ -93,6 +80,17 @@ typedef struct pjsip_method
} pjsip_method;
+/*
+ * For convenience, standard method structures are defined in the library.
+ */
+extern const pjsip_method pjsip_invite_method; /**< INVITE structure. */
+extern const pjsip_method pjsip_cancel_method; /**< CANCEL structure. */
+extern const pjsip_method pjsip_ack_method; /**< ACK structure. */
+extern const pjsip_method pjsip_bye_method; /**< BYE structure. */
+extern const pjsip_method pjsip_register_method; /**< REGISTER structure.*/
+extern const pjsip_method pjsip_options_method; /**< OPTIONS structure. */
+
+
/**
* Initialize the method structure from a string.
* This function will check whether the method is a known method then set
@@ -638,8 +636,8 @@ PJ_DECL(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type);
* @return The header field, or NULL if no header with the specified
* type is found.
*/
-PJ_DECL(void*) pjsip_msg_find_hdr( pjsip_msg *msg,
- pjsip_hdr_e type, void *start);
+PJ_DECL(void*) pjsip_msg_find_hdr( const pjsip_msg *msg,
+ pjsip_hdr_e type, const void *start);
/**
* Find a header in the message by its name.
@@ -654,8 +652,9 @@ PJ_DECL(void*) pjsip_msg_find_hdr( pjsip_msg *msg,
* @return The header field, or NULL if no header with the specified
* type is found.
*/
-PJ_DECL(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg,
- const pj_str_t *name, void *start);
+PJ_DECL(void*) pjsip_msg_find_hdr_by_name( const pjsip_msg *msg,
+ const pj_str_t *name,
+ const void *start);
/**
* Find and remove a header in the message.
@@ -702,7 +701,8 @@ PJ_IDECL(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr );
* @return The length of the printed characters (in bytes), or NEGATIVE
* value if the message is too large for the specified buffer.
*/
-PJ_DECL(pj_ssize_t) pjsip_msg_print(pjsip_msg *msg, char *buf, pj_size_t size);
+PJ_DECL(pj_ssize_t) pjsip_msg_print(const pjsip_msg *msg,
+ char *buf, pj_size_t size);
/**
* @}
diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h
index 14175dc7..6e6ba428 100644
--- a/pjsip/include/pjsip/sip_transport.h
+++ b/pjsip/include/pjsip/sip_transport.h
@@ -367,16 +367,21 @@ PJ_DECL(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata );
/**
* Decrement reference counter of the transmit buffer.
- * When the transmit buffer is no longer used, it will be destroyed.
+ * When the transmit buffer is no longer used, it will be destroyed and
+ * caller is informed with PJSIP_EBUFDESTROYED return status.
*
* @param tdata The transmit buffer data.
+ * @return This function will always succeeded eventhough the return
+ * status is non-zero. A status PJSIP_EBUFDESTROYED will be
+ * returned to inform that buffer is destroyed.
*/
-PJ_DECL(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata );
+PJ_DECL(pj_status_t) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata );
/**
* Check if transmit data buffer contains a valid message.
*
* @param tdata The transmit buffer.
+ * @return Non-zero (PJ_TRUE) if buffer contains a valid message.
*/
PJ_DECL(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata );
@@ -542,11 +547,14 @@ struct pjsip_tpfactory
/**
* Create new outbound connection.
+ * Note that the factory is responsible for both creating the
+ * transport and registering it to the transport manager.
*/
pj_status_t (*create_transport)(pjsip_tpfactory *factory,
pjsip_tpmgr *mgr,
pjsip_endpoint *endpt,
const pj_sockaddr *rem_addr,
+ int addr_len,
pjsip_transport **transport);
/*
@@ -621,11 +629,11 @@ PJ_DECL(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr);
* Find transport to be used to send message to remote destination. If no
* suitable transport is found, a new one will be created.
*/
-PJ_DECL(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr,
- pjsip_transport_type_e type,
- const pj_sockaddr_t *remote,
- int addr_len,
- pjsip_transport **p_transport );
+PJ_DECL(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_t *remote,
+ int addr_len,
+ pjsip_transport **tp);
/**
diff --git a/pjsip/include/pjsip/sip_transport_udp.h b/pjsip/include/pjsip/sip_transport_udp.h
index d3fd5d30..886f18c6 100644
--- a/pjsip/include/pjsip/sip_transport_udp.h
+++ b/pjsip/include/pjsip/sip_transport_udp.h
@@ -21,7 +21,7 @@
#include <pjsip/sip_transport.h>
-PJ_DECL
+PJ_BEGIN_DECL
/**
* Start UDP transport.
diff --git a/pjsip/include/pjsip/sip_util.h b/pjsip/include/pjsip/sip_util.h
index f55cd7e9..ea0fcf3f 100644
--- a/pjsip/include/pjsip/sip_util.h
+++ b/pjsip/include/pjsip/sip_util.h
@@ -20,6 +20,7 @@
#define __PJSIP_SIP_MISC_H__
#include <pjsip/sip_msg.h>
+#include <pjsip/sip_resolve.h>
PJ_BEGIN_DECL
@@ -34,6 +35,15 @@ PJ_BEGIN_DECL
* request outside a dialog, such as OPTIONS, MESSAGE, etc. To create a request
* inside a dialog, application should use #pjsip_dlg_create_request.
*
+ * This function adds the following headers in the request:
+ * - From, To, Call-ID, and CSeq,
+ * - Contact header, if contact is specified.
+ * - A blank Via header.
+ * - Additional request headers (such as Max-Forwards) which are copied
+ * from endpoint configuration.
+ *
+ * In addition, the function adds a unique tag in the From header.
+ *
* Once a transmit data is created, the reference counter is initialized to 1.
*
* @param endpt Endpoint instance.
@@ -62,7 +72,16 @@ PJ_DECL(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt,
/**
* Create an independent request message from the specified headers. This
- * function will shallow clone the headers and put them in the request.
+ * function will clone the headers and put them in the request.
+ *
+ * This function adds the following headers in the request:
+ * - From, To, Call-ID, and CSeq,
+ * - Contact header, if contact is specified.
+ * - A blank Via header.
+ * - Additional request headers (such as Max-Forwards) which are copied
+ * from endpoint configuration.
+ *
+ * In addition, the function adds a unique tag in the From header.
*
* Once a transmit data is created, the reference counter is initialized to 1.
*
@@ -92,31 +111,6 @@ pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
pjsip_tx_data **p_tdata);
/**
- * Send outgoing request and initiate UAC transaction for the request.
- * This is an auxiliary function to be used by application to send arbitrary
- * requests outside a dialog. To send a request within a dialog, application
- * should use #pjsip_dlg_send_msg instead.
- *
- * @param endpt The endpoint instance.
- * @param tdata The transmit data to be sent.
- * @param timeout Optional timeout for final response to be received, or -1
- * if the transaction should not have a timeout restriction.
- * @param token Optional token to be associated with the transaction, and
- * to be passed to the callback.
- * @param cb Optional callback to be called when the transaction has
- * received a final response. The callback will be called with
- * the previously registered token and the event that triggers
- * the completion of the transaction.
- *
- * @return PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- int timeout,
- void *token,
- void (*cb)(void*,pjsip_event*));
-
-/**
* Construct a minimal response message for the received request. This function
* will construct all the Via, Record-Route, Call-ID, From, To, CSeq, and
* Call-ID headers from the request.
@@ -125,14 +119,16 @@ PJ_DECL(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt,
*
* @param endpt The endpoint.
* @param rdata The request receive data.
- * @param code Status code to be put in the response.
+ * @param st_code Status code to be put in the response.
+ * @param st_text Optional status text, or NULL to get the default text.
* @param p_tdata Pointer to receive the transmit data.
*
* @return PJ_SUCCESS, or the appropriate error code.
*/
PJ_DECL(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
const pjsip_rx_data *rdata,
- int code,
+ int st_code,
+ const pj_str_t *st_text,
pjsip_tx_data **p_tdata);
/**
@@ -143,13 +139,16 @@ PJ_DECL(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
* this one.
*
* @param endpt The endpoint.
- * @param tdata On input, this contains the original INVITE request, and on
- * output, it contains the ACK message.
- * @param rdata The final response message.
+ * @param tdata This contains the original INVITE request
+ * @param rdata The final response.
+ * @param ack The ACK request created.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
*/
-PJ_DECL(void) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- const pjsip_rx_data *rdata );
+PJ_DECL(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
+ const pjsip_tx_data *tdata,
+ const pjsip_rx_data *rdata,
+ pjsip_tx_data **ack);
/**
@@ -162,25 +161,197 @@ PJ_DECL(void) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
* @return PJ_SUCCESS, or the appropriate error code.
*/
PJ_DECL(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
+ const pjsip_tx_data *tdata,
pjsip_tx_data **p_tdata);
/**
- * Get the address parameters (host, port, flag, TTL, etc) to send the
- * response.
+ * Find which destination to be used to send the request message, based
+ * on the request URI and Route headers in the message. The procedure
+ * used here follows the guidelines on sending the request in RFC 3261
+ * chapter 8.1.2.
+ *
+ * This function may modify the message (request line and Route headers),
+ * if the Route information specifies strict routing and the request
+ * URI in the message is different than the calculated target URI. In that
+ * case, the target URI will be put as the request URI of the request and
+ * current request URI will be put as the last entry of the Route headers.
+ *
+ * @param tdata The transmit data containing the request message.
+ * @param dest_info On return, it contains information about destination
+ * host to contact, along with the preferable transport
+ * type, if any. Caller will then normally proceed with
+ * resolving this host with server resolution procedure
+ * described in RFC 3263.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_get_request_addr( pjsip_tx_data *tdata,
+ pjsip_host_info *dest_info );
+
+
+/**
+ * This structure holds the state of outgoing stateless request.
+ */
+typedef struct pjsip_send_state
+{
+ /** Application token, which was specified when the function
+ * #pjsip_endpt_send_request_stateless() is called.
+ */
+ void *token;
+
+ /** Endpoint instance.
+ */
+ pjsip_endpoint *endpt;
+
+ /** Transmit data buffer being sent.
+ */
+ pjsip_tx_data *tdata;
+
+ /** Server addresses resolved.
+ */
+ pjsip_server_addresses addr;
+
+ /** Current server address being tried.
+ */
+ unsigned cur_addr;
+
+ /** Current transport being used.
+ */
+ pjsip_transport *cur_transport;
+
+ /** The application callback which was specified when the function
+ * #pjsip_endpt_send_request_stateless() was called.
+ */
+ void (*app_cb)(struct pjsip_send_state*,
+ pj_ssize_t sent,
+ pj_bool_t *cont);
+} pjsip_send_state;
+
+/**
+ * Send outgoing request statelessly The function will take care of which
+ * destination and transport to use based on the information in the message,
+ * taking care of URI in the request line and Route header.
+ *
+ * This function is different than #pjsip_transport_send() in that this
+ * function adds/modify the Via header as necessary.
+ *
+ * @param endpt The endpoint instance.
+ * @param tdata The transmit data to be sent.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t)
+pjsip_endpt_send_request_stateless( pjsip_endpoint *endpt,
+ pjsip_tx_data *tdata,
+ void *token,
+ void (*cb)(pjsip_send_state*,
+ pj_ssize_t sent,
+ pj_bool_t *cont));
+
+/**
+ * This structure describes destination information to send response.
+ * It is initialized by calling #pjsip_get_response_addr().
+ *
+ * If the response message should be sent using transport from which
+ * the request was received, then transport, addr, and addr_len fields
+ * are initialized.
+ *
+ * The dst_host field is also initialized. It should be used when server
+ * fails to send the response using the transport from which the request
+ * was received, or when the transport is NULL, which means server
+ * must send the response to this address (this situation occurs when
+ * maddr parameter is set, or when rport param is not set in the request).
+ */
+typedef struct pjsip_response_addr
+{
+ pjsip_transport *transport; /**< Immediate transport to be used. */
+ pj_sockaddr addr; /**< Immediate address to send to. */
+ int addr_len; /**< Address length. */
+ pjsip_host_info dst_host; /**< Destination host to contact. */
+} pjsip_response_addr;
+
+/**
+ * Determine which address (and transport) to use to send response message
+ * based on the received request. This function follows the specification
+ * in section 18.2.2 of RFC 3261 and RFC 3581 for calculating the destination
+ * address and transport.
+ *
+ * The information about destination to send the response will be returned
+ * in res_addr argument. Please see #pjsip_response_addr for more info.
*
* @param pool The pool.
- * @param tr The transport where the request was received.
- * @param via The top-most Via header of the request.
- * @param addr The send address concluded from the calculation.
+ * @param rdata The incoming request received by the server.
+ * @param res_addr On return, it will be initialized with information about
+ * destination address and transport to send the response.
*
* @return zero (PJ_OK) if successfull.
*/
PJ_DECL(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
- const pjsip_transport *tr,
- const pjsip_via_hdr *via,
- pjsip_host_info *addr);
+ pjsip_rx_data *rdata,
+ pjsip_response_addr *res_addr);
+
+/**
+ * Send response in tdata statelessly. The function will take care of which
+ * response destination and transport to use based on the information in the
+ * Via header (such as the presence of rport, symmetric transport, etc.).
+ *
+ * This function will create a new ephemeral transport if no existing
+ * transports can be used to send the message to the destination. The ephemeral
+ * transport will be destroyed after some period if it is not used to send any
+ * more messages.
+ *
+ * The behavior of this function complies with section 18.2.2 of RFC 3261
+ * and RFC 3581.
+ *
+ * @param endpt The endpoint instance.
+ * @param res_addr The information about the address and transport to send
+ * the response to. Application can get this information
+ * by calling #pjsip_get_response_addr().
+ * @param tdata The response message to be sent.
+ * @param token Token to be passed back when the callback is called.
+ * @param cb Optional callback to notify the transmission status
+ * to application, and to inform whether next address or
+ * transport will be tried.
+ *
+ * @return PJ_SUCCESS if response has been successfully created and
+ * sent to transport layer, or a non-zero error code.
+ * However, even when it returns PJ_SUCCESS, there is no
+ * guarantee that the response has been successfully sent.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_send_response( pjsip_endpoint *endpt,
+ pjsip_response_addr *res_addr,
+ pjsip_tx_data *tdata,
+ void *token,
+ void (*cb)(pjsip_send_state*,
+ pj_ssize_t sent,
+ pj_bool_t *cont));
+
+/**
+ * Send outgoing request and initiate UAC transaction for the request.
+ * This is an auxiliary function to be used by application to send arbitrary
+ * requests outside a dialog. To send a request within a dialog, application
+ * should use #pjsip_dlg_send_msg instead.
+ *
+ * @param endpt The endpoint instance.
+ * @param tdata The transmit data to be sent.
+ * @param timeout Optional timeout for final response to be received, or -1
+ * if the transaction should not have a timeout restriction.
+ * @param token Optional token to be associated with the transaction, and
+ * to be passed to the callback.
+ * @param cb Optional callback to be called when the transaction has
+ * received a final response. The callback will be called with
+ * the previously registered token and the event that triggers
+ * the completion of the transaction.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt,
+ pjsip_tx_data *tdata,
+ int timeout,
+ void *token,
+ void (*cb)(void*,pjsip_event*));
+
/**
* @}
diff --git a/pjsip/include/pjsip_core.h b/pjsip/include/pjsip_core.h
index 8ae286a5..9f3c7e9b 100644
--- a/pjsip/include/pjsip_core.h
+++ b/pjsip/include/pjsip_core.h
@@ -31,6 +31,7 @@
#include <pjsip/sip_tel_uri.h>
#include <pjsip/sip_transaction.h>
#include <pjsip/sip_transport.h>
+#include <pjsip/sip_transport_udp.h>
#include <pjsip/sip_uri.h>
#include <pjsip/sip_util.h>
diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c
index b3fa2f8c..61741cb1 100644
--- a/pjsip/src/pjsip/sip_endpoint.c
+++ b/pjsip/src/pjsip/sip_endpoint.c
@@ -56,12 +56,6 @@ struct pjsip_endpoint
/** Name. */
pj_str_t name;
- /** Transaction table. */
- pj_hash_table_t *tsx_table;
-
- /** Mutex for transaction table. */
- pj_mutex_t *tsx_table_mutex;
-
/** Timer heap. */
pj_timer_heap_t *timer_heap;
@@ -74,12 +68,15 @@ struct pjsip_endpoint
/** DNS Resolver. */
pjsip_resolver_t *resolver;
- /** Number of modules registered. */
- pj_uint32_t mod_count;
+ /** Modules lock. */
+ pj_rwmutex_t *mod_mutex;
/** Modules. */
pjsip_module *modules[PJSIP_MAX_MODULE];
+ /** Module list, sorted by priority. */
+ pjsip_module module_list;
+
/** Number of supported methods. */
unsigned method_cnt;
@@ -127,167 +124,119 @@ static void pool_callback( pj_pool_t *pool, pj_size_t size )
/*
- * Initialize modules.
+ * Register new module to the endpoint.
+ * The endpoint will then call the load and start function in the module to
+ * properly initialize the module, and assign a unique module ID for the
+ * module.
*/
-static pj_status_t init_modules( pjsip_endpoint *endpt )
+PJ_DEF(pj_status_t) pjsip_endpt_register_module( pjsip_endpoint *endpt,
+ pjsip_module *mod )
{
- pj_status_t status;
- unsigned i;
- //pj_str_t str_COMMA = { ", ", 2 };
- extern pjsip_module aux_tsx_module;
-
- PJ_LOG(5, (THIS_FILE, "init_modules()"));
-
- /* Load static modules. */
- endpt->mod_count = PJSIP_MAX_MODULE;
- status = register_static_modules( &endpt->mod_count, endpt->modules );
- if (status != 0) {
- return status;
- }
+ pj_status_t status = PJ_SUCCESS;
+ pjsip_module *m;
+ int i;
- /* Add mini aux module. */
- endpt->modules[endpt->mod_count++] = &aux_tsx_module;
+ pj_rwmutex_lock_write(endpt->mod_mutex);
- /* Load dynamic modules. */
- // Not supported yet!
+ /* Make sure that this module has not been registered. */
+ PJ_ASSERT_ON_FAIL( pj_list_find_node(&endpt->module_list, mod) == NULL,
+ {status = PJ_EEXISTS; goto on_return;});
- /* Sort modules on the priority. */
- for (i=endpt->mod_count-1; i>0; --i) {
- pj_uint32_t max = 0;
- unsigned j;
- for (j=1; j<=i; ++j) {
- if (endpt->modules[j]->priority > endpt->modules[max]->priority)
- max = j;
- }
- if (max != i) {
- pjsip_module *temp = endpt->modules[max];
- endpt->modules[max] = endpt->modules[i];
- endpt->modules[i] = temp;
- }
+ /* Find unused ID for this module. */
+ for (i=0; i<PJ_ARRAY_SIZE(endpt->modules); ++i) {
+ if (endpt->modules[i] == NULL)
+ break;
+ }
+ if (i == PJ_ARRAY_SIZE(endpt->modules)) {
+ pj_assert(!"Too many modules registered!");
+ status = PJ_ETOOMANY;
+ goto on_return;
}
- /* Initialize each module. */
- for (i=0; i < endpt->mod_count; ++i) {
- int j;
-
- pjsip_module *mod = endpt->modules[i];
- if (mod->init_module) {
- status = mod->init_module(endpt, mod, i);
- if (status != 0) {
- return status;
- }
- }
+ /* Assign the ID. */
+ mod->id = i;
- /* Collect all supported methods from modules. */
- for (j=0; j<mod->method_cnt; ++j) {
- unsigned k;
- for (k=0; k<endpt->method_cnt; ++k) {
- if (pjsip_method_cmp(mod->methods[j], endpt->methods[k]) == 0)
- break;
- }
- if (k == endpt->method_cnt) {
- if (endpt->method_cnt < MAX_METHODS) {
- endpt->methods[endpt->method_cnt++] = mod->methods[j];
- } else {
- PJ_LOG(1,(THIS_FILE, "Too many methods"));
- return -1;
- }
- }
- }
+ /* Try to load the module. */
+ if (mod->load) {
+ status = (*mod->load)(endpt);
+ if (status != PJ_SUCCESS)
+ goto on_return;
}
- /* Create Allow header. */
- endpt->allow_hdr = pjsip_allow_hdr_create( endpt->pool );
- endpt->allow_hdr->count = endpt->method_cnt;
- for (i=0; i<endpt->method_cnt; ++i) {
- endpt->allow_hdr->values[i] = endpt->methods[i]->name;
+ /* Try to start the module. */
+ if (mod->start) {
+ status = (*mod->start)();
+ if (status != PJ_SUCCESS)
+ goto on_return;
}
- /* Start each module. */
- for (i=0; i < endpt->mod_count; ++i) {
- pjsip_module *mod = endpt->modules[i];
- if (mod->start_module) {
- status = mod->start_module(mod);
- if (status != 0) {
- return status;
- }
- }
+ /* Save the module. */
+ endpt->modules[i] = mod;
+
+ /* Put in the module list, sorted by priority. */
+ m = endpt->module_list.next;
+ while (m != &endpt->module_list) {
+ if (m->priority > mod->priority)
+ break;
+ m = m->next;
}
+ pj_list_insert_before(m, mod);
/* Done. */
- return 0;
+ PJ_TODO(BUILD_ALLOW_HEADER_BASED_ON_MODULES_SUPPORTED_METHODS);
+
+on_return:
+ pj_rwmutex_unlock_write(endpt->mod_mutex);
+ return status;
}
/*
- * Unregister the transaction from the hash table, and destroy the resources
- * from the transaction.
+ * Unregister a module from the endpoint.
+ * The endpoint will then call the stop and unload function in the module to
+ * properly shutdown the module.
*/
-PJ_DEF(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt,
- pjsip_transaction *tsx)
+PJ_DEF(pj_status_t) pjsip_endpt_unregister_module( pjsip_endpoint *endpt,
+ pjsip_module *mod )
{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_tsx(%s)", tsx->obj_name));
-
- pj_assert(tsx->state == PJSIP_TSX_STATE_DESTROYED);
-
- /* No need to lock transaction.
- * This function typically is called from the transaction callback, which
- * means that transaction mutex is being held.
- */
- pj_assert( pj_mutex_is_locked(tsx->mutex) );
-
- /* Lock endpoint. */
- pj_mutex_lock( endpt->tsx_table_mutex );
+ pj_status_t status;
- /* Unregister from the hash table. */
- pj_hash_set( NULL, endpt->tsx_table, tsx->transaction_key.ptr,
- tsx->transaction_key.slen, NULL);
+ pj_rwmutex_lock_write(endpt->mod_mutex);
- /* Unlock endpoint mutex. */
- pj_mutex_unlock( endpt->tsx_table_mutex );
+ /* Make sure the module exists in the list. */
+ PJ_ASSERT_ON_FAIL( pj_list_find_node(&endpt->module_list, mod) == mod,
+ {status = PJ_ENOTFOUND;goto on_return;} );
- /* Destroy transaction mutex. */
- pj_mutex_destroy( tsx->mutex );
+ /* Make sure the module exists in the array. */
+ PJ_ASSERT_ON_FAIL( mod->id>=0 && mod->id<PJ_ARRAY_SIZE(endpt->modules) &&
+ endpt->modules[mod->id] == mod,
+ {status = PJ_ENOTFOUND; goto on_return;});
- /* Release the pool for the transaction. */
- pj_pool_release(tsx->pool);
+ /* Try to stop the module. */
+ if (mod->stop) {
+ status = (*mod->stop)();
+ if (status != PJ_SUCCESS) goto on_return;
+ }
- PJ_LOG(4, (THIS_FILE, "tsx%p destroyed", tsx));
-}
+ /* Try to unload the module. */
+ if (mod->unload) {
+ status = (*mod->unload)();
+ if (status != PJ_SUCCESS) goto on_return;
+ }
+ /* Remove module from array. */
+ endpt->modules[mod->id] = NULL;
-/*
- * Receive transaction events from transactions and dispatch them to the
- * modules.
- */
-static void endpt_do_event( pjsip_endpoint *endpt, pjsip_event *evt)
-{
- unsigned i;
+ /* Remove module from list. */
+ pj_list_erase(mod);
- /* Dispatch event to modules. */
- for (i=0; i<endpt->mod_count; ++i) {
- pjsip_module *mod = endpt->modules[i];
- if (mod && mod->tsx_handler) {
- mod->tsx_handler( mod, evt );
- }
- }
+ /* Done. */
+ status = PJ_SUCCESS;
- /* Destroy transaction if it is terminated. */
- if (evt->type == PJSIP_EVENT_TSX_STATE &&
- evt->body.tsx_state.tsx->state == PJSIP_TSX_STATE_DESTROYED)
- {
- /* No need to lock mutex. Mutex is locked inside the destroy function */
- pjsip_endpt_destroy_tsx( endpt, evt->body.tsx_state.tsx );
- }
-}
+ PJ_TODO(REMOVE_METHODS_FROM_ALLOW_HEADER_WHEN_MODULE_IS_UNREGISTERED);
-/*
- * Receive transaction events from transactions and put in the event queue
- * to be processed later.
- */
-void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt )
-{
- // Need to protect this with try/catch?
- endpt_do_event(endpt, evt);
+on_return:
+ pj_rwmutex_unlock_write(endpt->mod_mutex);
+ return status;
}
/*
@@ -375,10 +324,18 @@ PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf,
return PJ_ENOMEM;
/* Create endpoint. */
- endpt = pj_pool_calloc(pool, 1, sizeof(*endpt));
+ endpt = pj_pool_zalloc(pool, sizeof(*endpt));
endpt->pool = pool;
endpt->pf = pf;
+ /* Init modules list. */
+ pj_list_init(&endpt->module_list);
+
+ /* Create R/W mutex for module manipulation. */
+ status = pj_rwmutex_create(endpt->pool, "ept%p", &endpt->mod_mutex);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
/* Init parser. */
init_sip_parser();
@@ -399,20 +356,6 @@ PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf,
goto on_error;
}
- /* Create mutex for the transaction table. */
- status = pj_mutex_create_recursive( endpt->pool, "mtbl%p",
- &endpt->tsx_table_mutex);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- /* Create hash table for transaction. */
- endpt->tsx_table = pj_hash_create( endpt->pool, PJSIP_MAX_TSX_COUNT );
- if (!endpt->tsx_table) {
- status = PJ_ENOMEM;
- goto on_error;
- }
-
/* Create timer heap to manage all timers within this endpoint. */
status = pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT,
&endpt->timer_heap);
@@ -452,13 +395,6 @@ PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf,
goto on_error;
}
- /* Initialize TLS ID for transaction lock. */
- status = pj_thread_local_alloc(&pjsip_tsx_lock_tls_id);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
- pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL);
-
/* Initialize request headers. */
pj_list_init(&endpt->req_hdr);
@@ -470,13 +406,6 @@ PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf,
mf_hdr->ivalue = PJSIP_MAX_FORWARDS_VALUE;
pj_list_insert_before( &endpt->req_hdr, mf_hdr);
- /* Load and init modules. */
- status = init_modules(endpt);
- if (status != PJ_SUCCESS) {
- PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error in init_modules()"));
- return status;
- }
-
/* Done. */
*p_endpt = endpt;
return status;
@@ -490,9 +419,9 @@ on_error:
pj_mutex_destroy(endpt->mutex);
endpt->mutex = NULL;
}
- if (endpt->tsx_table_mutex) {
- pj_mutex_destroy(endpt->tsx_table_mutex);
- endpt->tsx_table_mutex = NULL;
+ if (endpt->mod_mutex) {
+ pj_rwmutex_destroy(endpt->mod_mutex);
+ endpt->mod_mutex = NULL;
}
pj_pool_release( endpt->pool );
@@ -513,9 +442,6 @@ PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt)
/* Delete endpoint mutex. */
pj_mutex_destroy(endpt->mutex);
- /* Delete transaction table mutex. */
- pj_mutex_destroy(endpt->tsx_table_mutex);
-
/* Finally destroy pool. */
pj_pool_release(endpt->pool);
}
@@ -624,93 +550,6 @@ PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt,
}
/*
- * Create a new transaction.
- * Endpoint must then initialize the new transaction as either UAS or UAC, and
- * register it to the hash table.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create_tsx(pjsip_endpoint *endpt,
- pjsip_transaction **p_tsx)
-{
- pj_pool_t *pool;
-
- PJ_ASSERT_RETURN(endpt && p_tsx, PJ_EINVAL);
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tsx()"));
-
- /* Request one pool for the transaction. Mutex is locked there. */
- pool = pjsip_endpt_create_pool(endpt, "ptsx%p",
- PJSIP_POOL_LEN_TSX, PJSIP_POOL_INC_TSX);
- if (pool == NULL) {
- return PJ_ENOMEM;
- }
-
- /* Create the transaction. */
- return pjsip_tsx_create(pool, endpt, p_tsx);
-}
-
-/*
- * Register the transaction to the endpoint.
- * This will put the transaction to the transaction hash table. Before calling
- * this function, the transaction must be INITIALIZED as either UAS or UAC, so
- * that the transaction key is built.
- */
-PJ_DEF(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt,
- pjsip_transaction *tsx)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_register_tsx(%s)", tsx->obj_name));
-
- pj_assert(tsx->transaction_key.slen != 0);
- //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL);
-
- /* Lock hash table mutex. */
- pj_mutex_lock(endpt->tsx_table_mutex);
-
- /* Register the transaction to the hash table. */
- pj_hash_set( tsx->pool, endpt->tsx_table, tsx->transaction_key.ptr,
- tsx->transaction_key.slen, tsx);
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->tsx_table_mutex);
-}
-
-/*
- * Find transaction by the key.
- */
-PJ_DEF(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt,
- const pj_str_t *key )
-{
- pjsip_transaction *tsx;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_find_tsx()"));
-
- /* Start lock mutex in the endpoint. */
- pj_mutex_lock(endpt->tsx_table_mutex);
-
- /* Find the transaction in the hash table. */
- tsx = pj_hash_get( endpt->tsx_table, key->ptr, key->slen );
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->tsx_table_mutex);
-
- return tsx;
-}
-
-/*
- * Create key.
- */
-static void rdata_create_key( pjsip_rx_data *rdata)
-{
- pjsip_role_e role;
- if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
- role = PJSIP_ROLE_UAS;
- } else {
- role = PJSIP_ROLE_UAC;
- }
- pjsip_tsx_create_key(rdata->tp_info.pool, &rdata->endpt_info.key, role,
- &rdata->msg_info.cseq->method, rdata);
-}
-
-/*
* This is the callback that is called by the transport manager when it
* receives a message from the network.
*/
@@ -719,19 +558,17 @@ static void endpt_transport_callback( pjsip_endpoint *endpt,
pjsip_rx_data *rdata )
{
pjsip_msg *msg = rdata->msg_info.msg;
- pjsip_transaction *tsx;
- pj_bool_t a_new_transaction_just_been_created = PJ_FALSE;
PJ_LOG(5, (THIS_FILE, "endpt_transport_callback(rdata=%p)", rdata));
if (status != PJ_SUCCESS) {
- const char *src_addr = rdata->pkt_info.src_name;
- int port = rdata->pkt_info.src_port;
PJSIP_ENDPT_LOG_ERROR((endpt, "transport", status,
- "Src.addr=%s:%d, packet:--\n"
+ "Error processing packet from %s:%d, packet:--\n"
"%s\n"
- "-- end of packet. Error",
- src_addr, port, rdata->msg_info.msg_buf));
+ "-- end of packet.",
+ rdata->pkt_info.src_name,
+ rdata->pkt_info.src_port,
+ rdata->msg_info.msg_buf));
return;
}
@@ -740,7 +577,7 @@ static void endpt_transport_callback( pjsip_endpoint *endpt,
* Ref: RFC3261 Section 18.1.2 Receiving Response
*/
if (msg->type == PJSIP_RESPONSE_MSG) {
- const pj_str_t *addr_addr;
+ const pj_str_t *local_addr;
int port = rdata->msg_info.via->sent_by.port;
pj_bool_t mismatch = PJ_FALSE;
if (port == 0) {
@@ -748,8 +585,8 @@ static void endpt_transport_callback( pjsip_endpoint *endpt,
type = rdata->tp_info.transport->key.type;
port = pjsip_transport_get_default_port_for_type(type);
}
- addr_addr = &rdata->tp_info.transport->local_name.host;
- if (pj_strcmp(&rdata->msg_info.via->sent_by.host, addr_addr) != 0)
+ local_addr = &rdata->tp_info.transport->local_name.host;
+ if (pj_strcmp(&rdata->msg_info.via->sent_by.host, local_addr) != 0)
mismatch = PJ_TRUE;
else if (port != rdata->tp_info.transport->local_name.port) {
/* Port or address mismatch, we should discard response */
@@ -759,186 +596,71 @@ static void endpt_transport_callback( pjsip_endpoint *endpt,
* So we discard the response only if the port doesn't match
* both the port in sent-by and rport. We try to be lenient here!
*/
- if (rdata->msg_info.via->rport_param != rdata->tp_info.transport->local_name.port)
+ if (rdata->msg_info.via->rport_param !=
+ rdata->tp_info.transport->local_name.port)
mismatch = PJ_TRUE;
else {
- PJ_LOG(4,(THIS_FILE, "Response %p has mismatch port in sent-by"
- " but the rport parameter is correct",
- rdata));
+ PJ_LOG(4,(THIS_FILE, "Response %p from %s has mismatch port in "
+ "sent-by but the rport parameter is "
+ "correct",
+ rdata, rdata->pkt_info.src_name));
}
}
if (mismatch) {
- pjsip_event e;
-
- PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata, PJSIP_EINVALIDVIA);
- endpt_do_event( endpt, &e );
+ PJ_TODO(ENDPT_REPORT_WHEN_DROPPING_MESSAGE);
+ PJ_LOG(4,(THIS_FILE, "Dropping response from %s:%d because sent-by"
+ " is mismatch",
+ rdata->pkt_info.src_name,
+ rdata->pkt_info.src_port));
return;
}
- }
-
- /* Create key for transaction lookup. */
- rdata_create_key( rdata);
-
- /* Find the transaction for the received message. */
- PJ_LOG(5, (THIS_FILE, "finding tsx with key=%.*s",
- rdata->endpt_info.key.slen, rdata->endpt_info.key.ptr));
-
- /* Start lock mutex in the endpoint. */
- pj_mutex_lock(endpt->tsx_table_mutex);
-
- /* Find the transaction in the hash table. */
- tsx = pj_hash_get( endpt->tsx_table, rdata->endpt_info.key.ptr, rdata->endpt_info.key.slen );
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->tsx_table_mutex);
-
- /* If the transaction is not found... */
- if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) {
-
- /*
- * For response message, discard the message, except if the response is
- * an 2xx class response to INVITE, which in this case it must be
- * passed to TU to be acked.
- */
- if (msg->type == PJSIP_RESPONSE_MSG) {
-
- /* Inform TU about the 200 message, only if it's INVITE. */
- if (PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200) &&
- rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD)
- {
- pjsip_event e;
-
- /* Should not happen for UA. Tsx theoritically lives until
- * all responses are absorbed.
- */
- pj_assert(0);
-
- PJSIP_EVENT_INIT_RX_200_MSG(e, rdata);
- endpt_do_event( endpt, &e );
-
- } else {
- /* Just discard the response, inform TU. */
- pjsip_event e;
-
- PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata,
- PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_CALL_TSX_DOES_NOT_EXIST));
- endpt_do_event( endpt, &e );
- }
-
- /*
- * For non-ACK request message, create a new transaction.
- */
- } else if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
+ }
- pj_status_t status;
- /* Create transaction, mutex is locked there. */
- status = pjsip_endpt_create_tsx(endpt, &tsx);
- if (status != PJ_SUCCESS) {
- PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
- "Unable to create transaction"));
- return;
- }
+ /* Distribute to modules. */
+ pj_rwmutex_lock_read(endpt->mod_mutex);
- /* Initialize transaction as UAS. */
- pjsip_tsx_init_uas( tsx, rdata );
+ if (msg->type == PJSIP_REQUEST_MSG) {
+ pjsip_module *mod;
+ pj_bool_t handled = PJ_FALSE;
- /* Register transaction, mutex is locked there. */
- pjsip_endpt_register_tsx( endpt, tsx );
-
- a_new_transaction_just_been_created = PJ_TRUE;
+ mod = endpt->module_list.next;
+ while (mod != &endpt->module_list) {
+ if (mod->on_rx_request)
+ handled = (*mod->on_rx_request)(rdata);
+ if (handled)
+ break;
+ mod = mod->next;
}
- }
- /* If transaction is found (or newly created), pass the message.
- * Otherwise if it's an ACK request, pass directly to TU.
- */
- if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED) {
- /* Dispatch message to transaction. */
- pjsip_tsx_on_rx_msg( tsx, rdata );
-
- } else if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD) {
- /*
- * This is an ACK message, but the INVITE transaction could not
- * be found (possibly because the branch parameter in Via in ACK msg
- * is different than the branch in original INVITE). This happens with
- * SER!
- */
- pjsip_event event;
-
- PJSIP_EVENT_INIT_RX_ACK_MSG(event,rdata);
- endpt_do_event( endpt, &event );
- }
+ /* No module is able to handle the request. */
+ if (!handled) {
+ PJ_TODO(ENDPT_RESPOND_UNHANDLED_REQUEST);
+ PJ_LOG(4,(THIS_FILE, "Request from %s:%d was dropped/unhandled by"
+ " any modules"));
+ }
- /*
- * If a new request message has just been receieved, but no modules
- * seem to be able to handle the request message, then terminate the
- * transaction.
- *
- * Ideally for cases like "unsupported method", we should be able to
- * answer the request statelessly. But we can not do that since the
- * endpoint shoule be able to be used as both user agent and proxy stack,
- * and a proxy stack should be able to handle arbitrary methods.
- */
- if (a_new_transaction_just_been_created && tsx->status_code < 100) {
- /* Certainly no modules has sent any response message.
- * Check that any modules has attached a module data.
- */
- int i;
- for (i=0; i<PJSIP_MAX_MODULE; ++i) {
- if (tsx->module_data[i] != NULL) {
+ } else {
+ pjsip_module *mod;
+ pj_bool_t handled = PJ_FALSE;
+
+ mod = endpt->module_list.next;
+ while (mod != &endpt->module_list) {
+ if (mod->on_rx_response)
+ handled = (*mod->on_rx_response)(rdata);
+ if (handled)
break;
- }
+ mod = mod->next;
}
- if (i == PJSIP_MAX_MODULE) {
- /* No modules have attached itself to the transaction.
- * Terminate the transaction with 501/Not Implemented.
- */
- pjsip_tx_data *tdata;
- pj_status_t status;
-
- if (tsx->method.id == PJSIP_OPTIONS_METHOD) {
- status = pjsip_endpt_create_response(endpt, rdata, 200,
- &tdata);
- } else {
- status = pjsip_endpt_create_response(endpt, rdata,
- PJSIP_SC_METHOD_NOT_ALLOWED,
- &tdata);
- }
-
- if (status != PJ_SUCCESS) {
- PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
- "Unable to create response"));
- return;
- }
- if (endpt->allow_hdr) {
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, endpt->allow_hdr));
- }
- pjsip_tsx_on_tx_msg( tsx, tdata );
-
- } else {
- /*
- * If a module has registered itself in the transaction but it
- * hasn't responded the request, chances are the module wouldn't
- * respond to the request at all. We terminate the request here
- * with 500/Internal Server Error, to be safe.
- */
- pjsip_tx_data *tdata;
- pj_status_t status;
-
- status = pjsip_endpt_create_response(endpt, rdata, 500, &tdata);
- if (status != PJ_SUCCESS) {
- PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
- "Unable to create response"));
- return;
- }
-
- pjsip_tsx_on_tx_msg(tsx, tdata);
+ if (!handled) {
+ PJ_LOG(4,(THIS_FILE, "Response from %s:%d was dropped/unhandled by"
+ " any modules"));
}
}
+
+ pj_rwmutex_unlock_read(endpt->mod_mutex);
}
/*
@@ -983,15 +705,15 @@ PJ_DEF(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt)
/*
* Find/create transport.
*/
-PJ_DEF(pj_status_t) pjsip_endpt_alloc_transport( pjsip_endpoint *endpt,
+PJ_DEF(pj_status_t) pjsip_endpt_acquire_transport(pjsip_endpoint *endpt,
pjsip_transport_type_e type,
- const pj_sockaddr *remote,
+ const pj_sockaddr_t *remote,
int addr_len,
- pjsip_transport **p_transport)
+ pjsip_transport **transport)
{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_alloc_transport()"));
- return pjsip_tpmgr_alloc_transport( endpt->transport_mgr, type,
- remote, addr_len, p_transport);
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_acquire_transport()"));
+ return pjsip_tpmgr_acquire_transport(endpt->transport_mgr, type,
+ remote, addr_len, transport);
}
@@ -1049,8 +771,6 @@ PJ_DEF(void) pjsip_endpt_log_error( pjsip_endpoint *endpt,
PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail )
{
#if PJ_LOG_MAX_LEVEL >= 3
- unsigned count;
-
PJ_LOG(5, (THIS_FILE, "pjsip_endpt_dump()"));
/* Lock mutex. */
@@ -1066,53 +786,6 @@ PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail )
pj_pool_get_capacity(endpt->pool),
pj_pool_get_used_size(endpt->pool)));
- /* Transaction tables. */
- count = pj_hash_count(endpt->tsx_table);
- PJ_LOG(3, (THIS_FILE, " Number of transactions: %u", count));
-
- if (count && detail) {
- pj_hash_iterator_t it_val;
- pj_hash_iterator_t *it;
- pj_time_val now;
-
- PJ_LOG(3, (THIS_FILE, " Dumping transaction tables:"));
-
- pj_gettimeofday(&now);
- it = pj_hash_first(endpt->tsx_table, &it_val);
-
- while (it != NULL) {
- int timeout_diff;
-
- /* Get the transaction. No need to lock transaction's mutex
- * since we already hold endpoint mutex, so that no transactions
- * will be deleted.
- */
- pjsip_transaction *tsx = pj_hash_this(endpt->tsx_table, it);
-
- const char *role = (tsx->role == PJSIP_ROLE_UAS ? "UAS" : "UAC");
-
- if (tsx->timeout_timer._timer_id != -1) {
- if (tsx->timeout_timer._timer_value.sec > now.sec) {
- timeout_diff = tsx->timeout_timer._timer_value.sec - now.sec;
- } else {
- timeout_diff = now.sec - tsx->timeout_timer._timer_value.sec;
- timeout_diff = 0 - timeout_diff;
- }
- } else {
- timeout_diff = -1;
- }
-
- PJ_LOG(3, (THIS_FILE, " %s %s %10.*s %.9u %s t=%ds",
- tsx->obj_name, role,
- tsx->method.name.slen, tsx->method.name.ptr,
- tsx->cseq,
- pjsip_tsx_state_str(tsx->state),
- timeout_diff));
-
- it = pj_hash_next(endpt->tsx_table, it);
- }
- }
-
/* Transports.
*/
pjsip_tpmgr_dump_transports( endpt->transport_mgr );
diff --git a/pjsip/src/pjsip/sip_errno.c b/pjsip/src/pjsip/sip_errno.c
index 999156c6..f3c7319b 100644
--- a/pjsip/src/pjsip/sip_errno.c
+++ b/pjsip/src/pjsip/sip_errno.c
@@ -39,14 +39,20 @@ static const struct
{ PJSIP_EINVALIDSCHEME, "Invalid URI scheme" },
{ PJSIP_EMSGTOOLONG, "Message too long" },
{ PJSIP_EPARTIALMSG, "Partial message" },
+ { PJSIP_EMISSINGREQURI, "Missing Request-URI" },
{ PJSIP_EMISSINGHDR, "Missing required header(s)" },
+ { PJSIP_EMISSINGBODY, "Missing message body" },
{ PJSIP_EINVALIDVIA, "Invalid Via header" },
{ PJSIP_EMULTIPLEVIA, "Multiple Via headers in response" },
+ { PJSIP_EINVALIDREQURI, "Invalid Request URI" },
+ { PJSIP_ENOTREQUESTMSG, "Expecting request message"},
+ { PJSIP_ENOTRESPONSEMSG, "Expecting response message"},
/* Transport errors */
{ PJSIP_EUNSUPTRANSPORT, "Unsupported transport"},
{ PJSIP_EPENDINGTX, "Transmit buffer already pending"},
{ PJSIP_ERXOVERFLOW, "Rx buffer overflow"},
+ { PJSIP_EBUFDESTROYED, "Buffer destroyed"},
/* Transaction errors */
{ PJSIP_ETSXDESTROYED, "Transaction has been destroyed"},
diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c
index 2dc83dda..1d5ffe06 100644
--- a/pjsip/src/pjsip/sip_msg.c
+++ b/pjsip/src/pjsip/sip_msg.c
@@ -21,6 +21,7 @@
#include <pjsip/print_util.h>
#include <pj/string.h>
#include <pj/pool.h>
+#include <pj/assert.h>
/*
* Include inline definitions here if functions are NOT inlined.
@@ -29,14 +30,22 @@
# include <pjsip/sip_msg_i.h>
#endif
-static const pj_str_t method_names[] =
-{
- { "INVITE", 6 },
- { "CANCEL", 6 },
- { "ACK", 3 },
- { "BYE", 3 },
- { "REGISTER", 8 },
- { "OPTIONS", 7 },
+const pjsip_method
+ pjsip_invite_method = { PJSIP_INVITE_METHOD, { "INVITE",6 } },
+ pjsip_cancel_method = { PJSIP_CANCEL_METHOD, { "CANCEL",6 } },
+ pjsip_ack_method = { PJSIP_ACK_METHOD, { "ACK",3} },
+ pjsip_bye_method = { PJSIP_BYE_METHOD, { "BYE",3} },
+ pjsip_register_method = { PJSIP_REGISTER_METHOD, { "REGISTER",8} },
+ pjsip_options_method = { PJSIP_OPTIONS_METHOD, { "OPTIONS",7} };
+
+static const pj_str_t *method_names[] =
+{
+ &pjsip_invite_method.name,
+ &pjsip_cancel_method.name,
+ &pjsip_ack_method.name,
+ &pjsip_bye_method.name,
+ &pjsip_register_method.name,
+ &pjsip_options_method.name
};
const pj_str_t pjsip_hdr_names[] =
@@ -178,8 +187,9 @@ PJ_DEF(void) pjsip_method_init( pjsip_method *m,
PJ_DEF(void) pjsip_method_set( pjsip_method *m, pjsip_method_e me )
{
+ pj_assert(me < PJSIP_OTHER_METHOD);
m->id = me;
- m->name = method_names[me];
+ m->name = *method_names[me];
}
PJ_DEF(void) pjsip_method_init_np(pjsip_method *m,
@@ -187,9 +197,9 @@ PJ_DEF(void) pjsip_method_init_np(pjsip_method *m,
{
int i;
for (i=0; i<PJ_ARRAY_SIZE(method_names); ++i) {
- if (pj_stricmp(str, &method_names[i])==0) {
+ if (pj_stricmp(str, method_names[i])==0) {
m->id = (pjsip_method_e)i;
- m->name = method_names[i];
+ m->name = *method_names[i];
return;
}
}
@@ -235,25 +245,26 @@ PJ_DEF(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type)
return msg;
}
-PJ_DEF(void*) pjsip_msg_find_hdr( pjsip_msg *msg,
- pjsip_hdr_e hdr_type, void *start)
+PJ_DEF(void*) pjsip_msg_find_hdr( const pjsip_msg *msg,
+ pjsip_hdr_e hdr_type, const void *start)
{
- pjsip_hdr *hdr=start, *end=&msg->hdr;
+ const pjsip_hdr *hdr=start, *end=&msg->hdr;
if (hdr == NULL) {
hdr = msg->hdr.next;
}
for (; hdr!=end; hdr = hdr->next) {
if (hdr->type == hdr_type)
- return hdr;
+ return (void*)hdr;
}
return NULL;
}
-PJ_DEF(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg,
- const pj_str_t *name, void *start)
+PJ_DEF(void*) pjsip_msg_find_hdr_by_name( const pjsip_msg *msg,
+ const pj_str_t *name,
+ const void *start)
{
- pjsip_hdr *hdr=start, *end=&msg->hdr;
+ const pjsip_hdr *hdr=start, *end=&msg->hdr;
if (hdr == NULL) {
hdr = msg->hdr.next;
@@ -261,10 +272,10 @@ PJ_DEF(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg,
for (; hdr!=end; hdr = hdr->next) {
if (hdr->type < PJSIP_H_OTHER) {
if (pj_stricmp(&pjsip_hdr_names[hdr->type], name) == 0)
- return hdr;
+ return (void*)hdr;
} else {
if (pj_stricmp(&hdr->name, name) == 0)
- return hdr;
+ return (void*)hdr;
}
}
return NULL;
@@ -280,7 +291,8 @@ PJ_DEF(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg,
return hdr;
}
-PJ_DEF(pj_ssize_t) pjsip_msg_print( pjsip_msg *msg, char *buf, pj_size_t size)
+PJ_DEF(pj_ssize_t) pjsip_msg_print( const pjsip_msg *msg,
+ char *buf, pj_size_t size)
{
char *p=buf, *end=buf+size;
int len;
@@ -1349,7 +1361,7 @@ PJ_DEF(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool )
{
pjsip_via_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
init_hdr(hdr, PJSIP_H_VIA, &via_hdr_vptr);
- hdr->sent_by.port = 5060;
+ //hdr->sent_by.port = 5060;
hdr->ttl_param = -1;
hdr->rport_param = -1;
pj_list_init(&hdr->other_param);
@@ -1383,9 +1395,11 @@ static int pjsip_via_hdr_print( pjsip_via_hdr *hdr,
*buf++ = ' ';
pj_memcpy(buf, hdr->sent_by.host.ptr, hdr->sent_by.host.slen);
buf += hdr->sent_by.host.slen;
- *buf++ = ':';
- printed = pj_utoa(hdr->sent_by.port, buf);
- buf += printed;
+ if (hdr->sent_by.port != 0) {
+ *buf++ = ':';
+ printed = pj_utoa(hdr->sent_by.port, buf);
+ buf += printed;
+ }
if (hdr->ttl_param >= 0) {
size = endbuf-buf;
diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c
index 7b163578..e7306846 100644
--- a/pjsip/src/pjsip/sip_parser.c
+++ b/pjsip/src/pjsip/sip_parser.c
@@ -1782,8 +1782,6 @@ static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx )
pj_scan_get_char(scanner);
pj_scan_get(scanner, &pjsip_DIGIT_SPEC, &digit);
hdr->sent_by.port = pj_strtoul(&digit);
- } else {
- hdr->sent_by.port = 5060;
}
int_parse_via_param(hdr, scanner, ctx->pool);
diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
index bb9eee9d..1497fbd6 100644
--- a/pjsip/src/pjsip/sip_transaction.c
+++ b/pjsip/src/pjsip/sip_transaction.c
@@ -30,6 +30,396 @@
#include <pj/pool.h>
#include <pj/assert.h>
+#if 0 // XXX JUNK
+ /* Initialize TLS ID for transaction lock. */
+ status = pj_thread_local_alloc(&pjsip_tsx_lock_tls_id);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+ pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL);
+
+
+ /* Create hash table for transaction. */
+ endpt->tsx_table = pj_hash_create( endpt->pool, PJSIP_MAX_TSX_COUNT );
+ if (!endpt->tsx_table) {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+
+
+/*
+ * Create a new transaction.
+ * Endpoint must then initialize the new transaction as either UAS or UAC, and
+ * register it to the hash table.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_create_tsx(pjsip_endpoint *endpt,
+ pjsip_transaction **p_tsx)
+{
+ pj_pool_t *pool;
+
+ PJ_ASSERT_RETURN(endpt && p_tsx, PJ_EINVAL);
+
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tsx()"));
+
+ /* Request one pool for the transaction. Mutex is locked there. */
+ pool = pjsip_endpt_create_pool(endpt, "ptsx%p",
+ PJSIP_POOL_LEN_TSX, PJSIP_POOL_INC_TSX);
+ if (pool == NULL) {
+ return PJ_ENOMEM;
+ }
+
+ /* Create the transaction. */
+ return pjsip_tsx_create(pool, endpt, p_tsx);
+}
+
+
+/*
+ * Register the transaction to the endpoint.
+ * This will put the transaction to the transaction hash table. Before calling
+ * this function, the transaction must be INITIALIZED as either UAS or UAC, so
+ * that the transaction key is built.
+ */
+PJ_DEF(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt,
+ pjsip_transaction *tsx)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_register_tsx(%s)", tsx->obj_name));
+
+ pj_assert(tsx->transaction_key.slen != 0);
+ //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL);
+
+ /* Lock hash table mutex. */
+ pj_mutex_lock(endpt->tsx_table_mutex);
+
+ /* Register the transaction to the hash table. */
+ pj_hash_set( tsx->pool, endpt->tsx_table, tsx->transaction_key.ptr,
+ tsx->transaction_key.slen, tsx);
+
+ /* Unlock mutex. */
+ pj_mutex_unlock(endpt->tsx_table_mutex);
+}
+
+/*
+ * Find transaction by the key.
+ */
+PJ_DEF(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt,
+ const pj_str_t *key )
+{
+ pjsip_transaction *tsx;
+
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_find_tsx()"));
+
+ /* Start lock mutex in the endpoint. */
+ pj_mutex_lock(endpt->tsx_table_mutex);
+
+ /* Find the transaction in the hash table. */
+ tsx = pj_hash_get( endpt->tsx_table, key->ptr, key->slen );
+
+ /* Unlock mutex. */
+ pj_mutex_unlock(endpt->tsx_table_mutex);
+
+ return tsx;
+}
+
+/*
+ * Create key.
+ */
+static void rdata_create_key( pjsip_rx_data *rdata)
+{
+ pjsip_role_e role;
+ if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
+ role = PJSIP_ROLE_UAS;
+ } else {
+ role = PJSIP_ROLE_UAC;
+ }
+ pjsip_tsx_create_key(rdata->tp_info.pool, &rdata->endpt_info.key, role,
+ &rdata->msg_info.cseq->method, rdata);
+}
+
+
+/*
+ * This is the callback that is called by the transport manager when it
+ * receives a message from the network.
+ */
+static void endpt_transport_callback( pjsip_endpoint *endpt,
+ pj_status_t status,
+ pjsip_rx_data *rdata )
+{
+ pjsip_msg *msg = rdata->msg_info.msg;
+ pjsip_transaction *tsx;
+ pj_bool_t a_new_transaction_just_been_created = PJ_FALSE;
+
+ PJ_LOG(5, (THIS_FILE, "endpt_transport_callback(rdata=%p)", rdata));
+
+ if (status != PJ_SUCCESS) {
+ const char *src_addr = rdata->pkt_info.src_name;
+ int port = rdata->pkt_info.src_port;
+ PJSIP_ENDPT_LOG_ERROR((endpt, "transport", status,
+ "Src.addr=%s:%d, packet:--\n"
+ "%s\n"
+ "-- end of packet. Error",
+ src_addr, port, rdata->msg_info.msg_buf));
+ return;
+ }
+
+ /* For response, check that the value in Via sent-by match the transport.
+ * If not matched, silently drop the response.
+ * Ref: RFC3261 Section 18.1.2 Receiving Response
+ */
+ if (msg->type == PJSIP_RESPONSE_MSG) {
+ const pj_str_t *addr_addr;
+ int port = rdata->msg_info.via->sent_by.port;
+ pj_bool_t mismatch = PJ_FALSE;
+ if (port == 0) {
+ int type;
+ type = rdata->tp_info.transport->key.type;
+ port = pjsip_transport_get_default_port_for_type(type);
+ }
+ addr_addr = &rdata->tp_info.transport->local_name.host;
+ if (pj_strcmp(&rdata->msg_info.via->sent_by.host, addr_addr) != 0)
+ mismatch = PJ_TRUE;
+ else if (port != rdata->tp_info.transport->local_name.port) {
+ /* Port or address mismatch, we should discard response */
+ /* But we saw one implementation (we don't want to name it to
+ * protect the innocence) which put wrong sent-by port although
+ * the "rport" parameter is correct.
+ * So we discard the response only if the port doesn't match
+ * both the port in sent-by and rport. We try to be lenient here!
+ */
+ if (rdata->msg_info.via->rport_param != rdata->tp_info.transport->local_name.port)
+ mismatch = PJ_TRUE;
+ else {
+ PJ_LOG(4,(THIS_FILE, "Response %p has mismatch port in sent-by"
+ " but the rport parameter is correct",
+ rdata));
+ }
+ }
+
+ if (mismatch) {
+ pjsip_event e;
+
+ PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata, PJSIP_EINVALIDVIA);
+ endpt_do_event( endpt, &e );
+ return;
+ }
+ }
+
+ /* Create key for transaction lookup. */
+ rdata_create_key( rdata);
+
+ /* Find the transaction for the received message. */
+ PJ_LOG(5, (THIS_FILE, "finding tsx with key=%.*s",
+ rdata->endpt_info.key.slen, rdata->endpt_info.key.ptr));
+
+ /* Start lock mutex in the endpoint. */
+ pj_mutex_lock(endpt->tsx_table_mutex);
+
+ /* Find the transaction in the hash table. */
+ tsx = pj_hash_get( endpt->tsx_table, rdata->endpt_info.key.ptr, rdata->endpt_info.key.slen );
+
+ /* Unlock mutex. */
+ pj_mutex_unlock(endpt->tsx_table_mutex);
+
+ /* If the transaction is not found... */
+ if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+
+ /*
+ * For response message, discard the message, except if the response is
+ * an 2xx class response to INVITE, which in this case it must be
+ * passed to TU to be acked.
+ */
+ if (msg->type == PJSIP_RESPONSE_MSG) {
+
+ /* Inform TU about the 200 message, only if it's INVITE. */
+ if (PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200) &&
+ rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD)
+ {
+ pjsip_event e;
+
+ /* Should not happen for UA. Tsx theoritically lives until
+ * all responses are absorbed.
+ */
+ pj_assert(0);
+
+ PJSIP_EVENT_INIT_RX_200_MSG(e, rdata);
+ endpt_do_event( endpt, &e );
+
+ } else {
+ /* Just discard the response, inform TU. */
+ pjsip_event e;
+
+ PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata,
+ PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_CALL_TSX_DOES_NOT_EXIST));
+ endpt_do_event( endpt, &e );
+ }
+
+ /*
+ * For non-ACK request message, create a new transaction.
+ */
+ } else if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
+
+ pj_status_t status;
+
+ /* Create transaction, mutex is locked there. */
+ status = pjsip_endpt_create_tsx(endpt, &tsx);
+ if (status != PJ_SUCCESS) {
+ PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
+ "Unable to create transaction"));
+ return;
+ }
+
+ /* Initialize transaction as UAS. */
+ pjsip_tsx_init_uas( tsx, rdata );
+
+ /* Register transaction, mutex is locked there. */
+ pjsip_endpt_register_tsx( endpt, tsx );
+
+ a_new_transaction_just_been_created = PJ_TRUE;
+ }
+ }
+
+ /* If transaction is found (or newly created), pass the message.
+ * Otherwise if it's an ACK request, pass directly to TU.
+ */
+ if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED) {
+ /* Dispatch message to transaction. */
+ pjsip_tsx_on_rx_msg( tsx, rdata );
+
+ } else if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD) {
+ /*
+ * This is an ACK message, but the INVITE transaction could not
+ * be found (possibly because the branch parameter in Via in ACK msg
+ * is different than the branch in original INVITE). This happens with
+ * SER!
+ */
+ pjsip_event event;
+
+ PJSIP_EVENT_INIT_RX_ACK_MSG(event,rdata);
+ endpt_do_event( endpt, &event );
+ }
+
+ /*
+ * If a new request message has just been receieved, but no modules
+ * seem to be able to handle the request message, then terminate the
+ * transaction.
+ *
+ * Ideally for cases like "unsupported method", we should be able to
+ * answer the request statelessly. But we can not do that since the
+ * endpoint shoule be able to be used as both user agent and proxy stack,
+ * and a proxy stack should be able to handle arbitrary methods.
+ */
+ if (a_new_transaction_just_been_created && tsx->status_code < 100) {
+ /* Certainly no modules has sent any response message.
+ * Check that any modules has attached a module data.
+ */
+ int i;
+ for (i=0; i<PJSIP_MAX_MODULE; ++i) {
+ if (tsx->module_data[i] != NULL) {
+ break;
+ }
+ }
+ if (i == PJSIP_MAX_MODULE) {
+ /* No modules have attached itself to the transaction.
+ * Terminate the transaction with 501/Not Implemented.
+ */
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ if (tsx->method.id == PJSIP_OPTIONS_METHOD) {
+ status = pjsip_endpt_create_response(endpt, rdata, 200,
+ &tdata);
+ } else {
+ status = pjsip_endpt_create_response(endpt, rdata,
+ PJSIP_SC_METHOD_NOT_ALLOWED,
+ &tdata);
+ }
+
+ if (status != PJ_SUCCESS) {
+ PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
+ "Unable to create response"));
+ return;
+ }
+
+ if (endpt->allow_hdr) {
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, endpt->allow_hdr));
+ }
+ pjsip_tsx_on_tx_msg( tsx, tdata );
+
+ } else {
+ /*
+ * If a module has registered itself in the transaction but it
+ * hasn't responded the request, chances are the module wouldn't
+ * respond to the request at all. We terminate the request here
+ * with 500/Internal Server Error, to be safe.
+ */
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ status = pjsip_endpt_create_response(endpt, rdata, 500, &tdata);
+ if (status != PJ_SUCCESS) {
+ PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
+ "Unable to create response"));
+ return;
+ }
+
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ }
+ }
+}
+
+
+
+ /* Transaction tables. */
+ count = pj_hash_count(endpt->tsx_table);
+ PJ_LOG(3, (THIS_FILE, " Number of transactions: %u", count));
+
+ if (count && detail) {
+ pj_hash_iterator_t it_val;
+ pj_hash_iterator_t *it;
+ pj_time_val now;
+
+ PJ_LOG(3, (THIS_FILE, " Dumping transaction tables:"));
+
+ pj_gettimeofday(&now);
+ it = pj_hash_first(endpt->tsx_table, &it_val);
+
+ while (it != NULL) {
+ int timeout_diff;
+
+ /* Get the transaction. No need to lock transaction's mutex
+ * since we already hold endpoint mutex, so that no transactions
+ * will be deleted.
+ */
+ pjsip_transaction *tsx = pj_hash_this(endpt->tsx_table, it);
+
+ const char *role = (tsx->role == PJSIP_ROLE_UAS ? "UAS" : "UAC");
+
+ if (tsx->timeout_timer._timer_id != -1) {
+ if (tsx->timeout_timer._timer_value.sec > now.sec) {
+ timeout_diff = tsx->timeout_timer._timer_value.sec - now.sec;
+ } else {
+ timeout_diff = now.sec - tsx->timeout_timer._timer_value.sec;
+ timeout_diff = 0 - timeout_diff;
+ }
+ } else {
+ timeout_diff = -1;
+ }
+
+ PJ_LOG(3, (THIS_FILE, " %s %s %10.*s %.9u %s t=%ds",
+ tsx->obj_name, role,
+ tsx->method.name.slen, tsx->method.name.ptr,
+ tsx->cseq,
+ pjsip_tsx_state_str(tsx->state),
+ timeout_diff));
+
+ it = pj_hash_next(endpt->tsx_table, it);
+ }
+ }
+
+
+
+#endif // XXX JUNK
+
/* Thread Local Storage ID for transaction lock (initialized by endpoint) */
long pjsip_tsx_lock_tls_id;
@@ -154,6 +544,45 @@ PJ_DEF(const char *) pjsip_role_name(pjsip_role_e role)
}
+
+/*
+ * Unregister the transaction from the hash table, and destroy the resources
+ * from the transaction.
+ */
+PJ_DEF(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt,
+ pjsip_transaction *tsx)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_tsx(%s)", tsx->obj_name));
+
+ pj_assert(tsx->state == PJSIP_TSX_STATE_DESTROYED);
+
+ /* No need to lock transaction.
+ * This function typically is called from the transaction callback, which
+ * means that transaction mutex is being held.
+ */
+ pj_assert( pj_mutex_is_locked(tsx->mutex) );
+
+ /* Lock endpoint. */
+ pj_mutex_lock( endpt->tsx_table_mutex );
+
+ /* Unregister from the hash table. */
+ pj_hash_set( NULL, endpt->tsx_table, tsx->transaction_key.ptr,
+ tsx->transaction_key.slen, NULL);
+
+ /* Unlock endpoint mutex. */
+ pj_mutex_unlock( endpt->tsx_table_mutex );
+
+ /* Destroy transaction mutex. */
+ pj_mutex_destroy( tsx->mutex );
+
+ /* Release the pool for the transaction. */
+ pj_pool_release(tsx->pool);
+
+ PJ_LOG(4, (THIS_FILE, "tsx%p destroyed", tsx));
+}
+
+
+
/*
* Create transaction key for RFC2543 compliant messages, which don't have
* unique branch parameter in the top most Via header.
@@ -500,9 +929,7 @@ static pj_status_t tsx_process_route( pjsip_transaction *tsx,
pjsip_tx_data *tdata,
pjsip_host_info *send_addr )
{
- const pjsip_uri *new_request_uri, *target_uri;
- const pjsip_name_addr *topmost_route_uri;
- pjsip_route_hdr *first_route_hdr, *last_route_hdr;
+ pjsip_route_hdr *route_hdr;
pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
@@ -510,130 +937,20 @@ static pj_status_t tsx_process_route( pjsip_transaction *tsx,
* have any "Route" headers but the endpoint has, then copy the "Route"
* headers from the endpoint first.
*/
- last_route_hdr = first_route_hdr =
- pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
- if (first_route_hdr) {
- topmost_route_uri = &first_route_hdr->name_addr;
- while (last_route_hdr->next != (void*)&tdata->msg->hdr) {
- pjsip_route_hdr *hdr;
- hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE,
- last_route_hdr->next);
- if (!hdr)
- break;
- last_route_hdr = hdr;
- }
- } else {
+ route_hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
+ if (!route_hdr) {
const pjsip_route_hdr *hdr_list;
- hdr_list = (pjsip_route_hdr*)pjsip_endpt_get_routing(tsx->endpt);
- if (hdr_list->next != hdr_list) {
- const pjsip_route_hdr *hdr = (pjsip_route_hdr*)hdr_list->next;
- first_route_hdr = NULL;
- topmost_route_uri = &hdr->name_addr;
- do {
- last_route_hdr = pjsip_hdr_shallow_clone(tdata->pool, hdr);
- if (first_route_hdr == NULL)
- first_route_hdr = last_route_hdr;
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)last_route_hdr);
- hdr = hdr->next;
- } while (hdr != hdr_list);
- } else {
- topmost_route_uri = NULL;
+ const pjsip_route_hdr *hdr;
+ hdr_list = (const pjsip_route_hdr*)pjsip_endpt_get_routing(tsx->endpt);
+ hdr = hdr_list->next;
+ while (hdr != hdr_list {
+ route_hdr = pjsip_hdr_shallow_clone(tdata->pool, hdr);
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route_hdr);
+ hdr = hdr->next;
}
}
- /* If Route headers exist, and the first element indicates loose-route,
- * the URI is taken from the Request-URI, and we keep all existing Route
- * headers intact.
- * If Route headers exist, and the first element DOESN'T indicate loose
- * route, the URI is taken from the first Route header, and remove the
- * first Route header from the message.
- * Otherwise if there's no Route headers, the URI is taken from the
- * Request-URI.
- */
- if (topmost_route_uri) {
- pj_bool_t has_lr_param;
-
- if (PJSIP_URI_SCHEME_IS_SIP(topmost_route_uri) ||
- PJSIP_URI_SCHEME_IS_SIPS(topmost_route_uri))
- {
- const pjsip_url *url = pjsip_uri_get_uri((void*)topmost_route_uri);
- has_lr_param = url->lr_param;
- } else {
- has_lr_param = 0;
- }
-
- if (has_lr_param) {
- new_request_uri = tdata->msg->line.req.uri;
- /* We shouldn't need to delete topmost Route if it has lr param.
- * But seems like it breaks some proxy implementation, so we
- * delete it anyway.
- */
- /*
- pj_list_erase(first_route_hdr);
- if (first_route_hdr == last_route_hdr)
- last_route_hdr = NULL;
- */
- } else {
- new_request_uri = pjsip_uri_get_uri((void*)topmost_route_uri);
- pj_list_erase(first_route_hdr);
- if (first_route_hdr == last_route_hdr)
- last_route_hdr = NULL;
- }
-
- target_uri = (pjsip_uri*)topmost_route_uri;
-
- } else {
- target_uri = new_request_uri = tdata->msg->line.req.uri;
- }
-
- /* The target URI must be a SIP/SIPS URL so we can resolve it's address.
- * Otherwise we're in trouble (i.e. there's no host part in tel: URL).
- */
- pj_memset(send_addr, 0, sizeof(*send_addr));
-
- if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) {
- pjsip_uri *uri = (pjsip_uri*) target_uri;
- const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
- send_addr->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE);
- pj_strdup(tdata->pool, &send_addr->addr.host, &url->host);
- send_addr->addr.port = url->port;
- send_addr->type =
- pjsip_transport_get_type_from_name(&url->transport_param);
-
- } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) {
- pjsip_uri *uri = (pjsip_uri*) target_uri;
- const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
- pj_strdup(tdata->pool, &send_addr->addr.host, &url->host);
- send_addr->addr.port = url->port;
- send_addr->type =
- pjsip_transport_get_type_from_name(&url->transport_param);
-#if PJ_HAS_TCP
- if (send_addr->type == PJSIP_TRANSPORT_TCP ||
- send_addr->type == PJSIP_TRANSPORT_SCTP)
- {
- send_addr->flag |= PJSIP_TRANSPORT_RELIABLE;
- }
-#endif
- } else {
- pj_assert(!"Unsupported URI scheme!");
- return PJSIP_EINVALIDSCHEME;
- }
-
- /* If target URI is different than request URI, replace
- * request URI add put the original URI in the last Route header.
- */
- if (new_request_uri && new_request_uri!=tdata->msg->line.req.uri) {
- pjsip_route_hdr *route = pjsip_route_hdr_create(tdata->pool);
- route->name_addr.uri = tdata->msg->line.req.uri;
- if (last_route_hdr)
- pj_list_insert_after(last_route_hdr, route);
- else
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route);
- tdata->msg->line.req.uri = (pjsip_uri*)new_request_uri;
- }
-
- /* Success. */
- return PJ_SUCCESS;
+ return pjsip_get_request_addr(tdata, send_addr);
}
diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
index 7299060c..50ff6be3 100644
--- a/pjsip/src/pjsip/sip_transport.c
+++ b/pjsip/src/pjsip/sip_transport.c
@@ -43,6 +43,9 @@ struct pjsip_tpmgr
pj_lock_t *lock;
pjsip_endpoint *endpt;
pjsip_tpfactory factory_list;
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+ pj_atomic_t *tdata_counter;
+#endif
void (*msg_cb)(pjsip_endpoint*, pj_status_t, pjsip_rx_data*);
};
@@ -204,6 +207,10 @@ PJ_DEF(pj_status_t) pjsip_tx_data_create( pjsip_tpmgr *mgr,
pj_ioqueue_op_key_init(&tdata->op_key.key, sizeof(tdata->op_key));
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+ pj_atomic_inc( tdata->mgr->tdata_counter );
+#endif
+
*p_tdata = tdata;
return PJ_SUCCESS;
}
@@ -221,14 +228,20 @@ PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata )
* Decrease transport data reference, destroy it when the reference count
* reaches zero.
*/
-PJ_DEF(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata )
+PJ_DEF(pj_status_t) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata )
{
pj_assert( pj_atomic_get(tdata->ref_cnt) > 0);
if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) {
PJ_LOG(5,(tdata->obj_name, "destroying txdata"));
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+ pj_atomic_dec( tdata->mgr->tdata_counter );
+#endif
pj_atomic_destroy( tdata->ref_cnt );
pj_lock_destroy( tdata->lock );
pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool );
+ return PJSIP_EBUFDESTROYED;
+ } else {
+ return PJ_SUCCESS;
}
}
@@ -452,7 +465,7 @@ PJ_DEF(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr,
/*
* Unregister timer, if any.
*/
- pj_assert(tp->idle_timer.id == PJ_FALSE);
+ //pj_assert(tp->idle_timer.id == PJ_FALSE);
if (tp->idle_timer.id != PJ_FALSE) {
pjsip_endpt_cancel_timer(mgr->endpt, &tp->idle_timer);
tp->idle_timer.id = PJ_FALSE;
@@ -566,6 +579,12 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool,
if (status != PJ_SUCCESS)
return status;
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+ status = pj_atomic_create(pool, 0, &mgr->tdata_counter);
+ if (status != PJ_SUCCESS)
+ return status;
+#endif
+
*p_mgr = mgr;
return PJ_SUCCESS;
}
@@ -582,6 +601,10 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_destroy( pjsip_tpmgr *mgr )
PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_destroy()"));
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+ pj_assert(pj_atomic_get(mgr->tdata_counter) == 0);
+#endif
+
pj_lock_acquire(mgr->lock);
itr = pj_hash_first(mgr->table, &itr_val);
@@ -600,6 +623,7 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_destroy( pjsip_tpmgr *mgr )
}
pj_lock_release(mgr->lock);
+ pj_lock_destroy(mgr->lock);
return PJ_SUCCESS;
}
@@ -741,16 +765,16 @@ finish_process_fragment:
/*
- * pjsip_tpmgr_alloc_transport()
+ * pjsip_tpmgr_acquire_transport()
*
* Get transport suitable to communicate to remote. Create a new one
* if necessary.
*/
-PJ_DEF(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr,
- pjsip_transport_type_e type,
- const pj_sockaddr_t *remote,
- int addr_len,
- pjsip_transport **p_transport)
+PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_t *remote,
+ int addr_len,
+ pjsip_transport **tp)
{
struct transport_key
{
@@ -798,7 +822,7 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr,
*/
pjsip_transport_add_ref(transport);
pj_lock_release(mgr->lock);
- *p_transport = transport;
+ *tp = transport;
return PJ_SUCCESS;
}
@@ -821,8 +845,12 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr,
/* Request factory to create transport. */
status = factory->create_transport(factory, mgr, mgr->endpt,
- remote, p_transport);
-
+ remote, addr_len, tp);
+ if (status == PJ_SUCCESS) {
+ PJ_ASSERT_ON_FAIL(tp!=NULL,
+ {pj_lock_release(mgr->lock); return PJ_EBUG;});
+ pjsip_transport_add_ref(*tp);
+ }
pj_lock_release(mgr->lock);
return status;
}
diff --git a/pjsip/src/pjsip/sip_transport_udp.c b/pjsip/src/pjsip/sip_transport_udp.c
index bd6d7ffb..f61b58c4 100644
--- a/pjsip/src/pjsip/sip_transport_udp.c
+++ b/pjsip/src/pjsip/sip_transport_udp.c
@@ -21,6 +21,7 @@
#include <pjsip/sip_errno.h>
#include <pj/pool.h>
#include <pj/sock.h>
+#include <pj/addr_resolv.h>
#include <pj/os.h>
#include <pj/lock.h>
#include <pj/string.h>
@@ -267,6 +268,9 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt,
unsigned i;
pj_status_t status;
+ PJ_ASSERT_RETURN(endpt && sock!=PJ_INVALID_SOCKET && a_name && async_cnt>0,
+ PJ_EINVAL);
+
/* Create pool. */
pool = pjsip_endpt_create_pool(endpt, "udp%p", PJSIP_POOL_LEN_TRANSPORT,
PJSIP_POOL_INC_TRANSPORT);
@@ -327,7 +331,7 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt,
pj_sprintf(tp->base.info, "udp %s:%d [published as %s:%d]",
pj_inet_ntoa(((pj_sockaddr_in*)&tp->base.local_addr)->sin_addr),
pj_ntohs(((pj_sockaddr_in*)&tp->base.local_addr)->sin_port),
- tp->base.local_name.host,
+ tp->base.local_name.host.ptr,
tp->base.local_name.port);
/* Set endpoint. */
@@ -367,6 +371,8 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt,
/* Create rdata and put it in the array. */
tp->rdata_cnt = 0;
+ tp->rdata = pj_pool_calloc(tp->base.pool, async_cnt,
+ sizeof(pjsip_rx_data*));
for (i=0; i<async_cnt; ++i) {
pj_pool_t *rdata_pool = pjsip_endpt_create_pool(endpt, "rtd%p",
PJSIP_POOL_RDATA_LEN,
@@ -382,6 +388,7 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt,
/* Init tp_info part. */
tp->rdata[i]->tp_info.pool = rdata_pool;
tp->rdata[i]->tp_info.transport = &tp->base;
+ tp->rdata[i]->tp_info.op_key.rdata = tp->rdata[i];
pj_ioqueue_op_key_init(&tp->rdata[i]->tp_info.op_key.op_key,
sizeof(pj_ioqueue_op_key_t));
@@ -436,6 +443,8 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_start( pjsip_endpoint *endpt,
char addr_buf[16];
pjsip_host_port bound_name;
+ PJ_ASSERT_RETURN(local != NULL, PJ_EINVAL);
+
status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock);
if (status != PJ_SUCCESS)
return status;
@@ -447,10 +456,31 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_start( pjsip_endpoint *endpt,
}
if (a_name == NULL) {
+ /* Address name is not specified.
+ * Build a name based on bound address.
+ */
a_name = &bound_name;
bound_name.host.ptr = addr_buf;
- pj_strcpy2(&bound_name.host, pj_inet_ntoa(local->sin_addr));
bound_name.port = pj_ntohs(local->sin_port);
+
+ /* If bound address specifies "0.0.0.0", get the IP address
+ * of local hostname.
+ */
+ if (local->sin_addr.s_addr == PJ_INADDR_ANY) {
+ pj_hostent he;
+ const pj_str_t *hostname = pj_gethostname();
+ status = pj_gethostbyname(hostname, &he);
+ if (status != PJ_SUCCESS) {
+ pj_sock_close(sock);
+ return status;
+ }
+ pj_strcpy2(&bound_name.host,
+ pj_inet_ntoa(*(pj_in_addr*)he.h_addr));
+ } else {
+ /* Otherwise use bound address. */
+ pj_strcpy2(&bound_name.host, pj_inet_ntoa(local->sin_addr));
+ }
+
}
return pjsip_udp_transport_attach( endpt, sock, a_name, async_cnt,
diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c
index c227eff7..532d6fb5 100644
--- a/pjsip/src/pjsip/sip_util.c
+++ b/pjsip/src/pjsip/sip_util.c
@@ -23,6 +23,7 @@
#include <pjsip/sip_event.h>
#include <pjsip/sip_transaction.h>
#include <pjsip/sip_module.h>
+#include <pjsip/sip_errno.h>
#include <pj/log.h>
#include <pj/string.h>
#include <pj/guid.h>
@@ -51,101 +52,6 @@ static const char *event_str[] =
static pj_str_t str_TEXT = { "text", 4},
str_PLAIN = { "plain", 5 };
-static int aux_mod_id;
-
-struct aux_tsx_data
-{
- void *token;
- void (*cb)(void*,pjsip_event*);
-};
-
-static pj_status_t aux_tsx_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id )
-{
- PJ_UNUSED_ARG(endpt);
- PJ_UNUSED_ARG(mod);
-
- aux_mod_id = id;
- return 0;
-}
-
-static void aux_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
-{
- pjsip_transaction *tsx;
- struct aux_tsx_data *tsx_data;
-
- PJ_UNUSED_ARG(mod);
-
- if (event->type != PJSIP_EVENT_TSX_STATE)
- return;
-
- pj_assert(event->body.tsx_state.tsx != NULL);
- tsx = event->body.tsx_state.tsx;
- if (tsx == NULL)
- return;
- if (tsx->module_data[aux_mod_id] == NULL)
- return;
- if (tsx->status_code < 200)
- return;
-
- /* Call the callback, if any, and prevent the callback to be called again
- * by clearing the transaction's module_data.
- */
- tsx_data = tsx->module_data[aux_mod_id];
- tsx->module_data[aux_mod_id] = NULL;
-
- if (tsx_data->cb) {
- (*tsx_data->cb)(tsx_data->token, event);
- }
-}
-
-pjsip_module aux_tsx_module =
-{
- { "Aux-Tsx", 7}, /* Name. */
- 0, /* Flag */
- 128, /* Priority */
- NULL, /* Arbitrary data. */
- 0, /* Number of methods supported (none). */
- { 0 }, /* Array of methods (none) */
- &aux_tsx_init, /* init_module() */
- NULL, /* start_module() */
- NULL, /* deinit_module() */
- &aux_tsx_handler, /* tsx_handler() */
-};
-
-PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- int timeout,
- void *token,
- void (*cb)(void*,pjsip_event*))
-{
- pjsip_transaction *tsx;
- struct aux_tsx_data *tsx_data;
- pj_status_t status;
-
- status = pjsip_endpt_create_tsx(endpt, &tsx);
- if (!tsx) {
- pjsip_tx_data_dec_ref(tdata);
- return -1;
- }
-
- tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data));
- tsx_data->token = token;
- tsx_data->cb = cb;
- tsx->module_data[aux_mod_id] = tsx_data;
-
- if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
- pjsip_endpt_destroy_tsx(endpt, tsx);
- pjsip_tx_data_dec_ref(tdata);
- return -1;
- }
-
- pjsip_endpt_register_tsx(endpt, tsx);
- pjsip_tx_data_invalidate_msg(tdata);
- pjsip_tsx_on_tx_msg(tsx, tdata);
- pjsip_tx_data_dec_ref(tdata);
- return 0;
-}
/*
* Initialize transmit data (msg) with the headers and optional body.
@@ -169,6 +75,7 @@ static void init_request_throw( pjsip_endpoint *endpt,
{
pjsip_msg *msg;
pjsip_msg_body *body;
+ pjsip_via_hdr *via;
const pjsip_hdr *endpt_hdr;
/* Create the message. */
@@ -205,6 +112,11 @@ static void init_request_throw( pjsip_endpoint *endpt,
/* Add CSeq header. */
pjsip_msg_add_hdr(msg, (void*)param_cseq);
+ /* Add a blank Via header. */
+ via = pjsip_via_hdr_create(tdata->pool);
+ via->rport_param = 0;
+ pjsip_msg_insert_first_hdr(msg, (void*)via);
+
/* Create message body. */
if (param_text) {
body = pj_pool_calloc(tdata->pool, 1, sizeof(pjsip_msg_body));
@@ -216,6 +128,13 @@ static void init_request_throw( pjsip_endpoint *endpt,
body->print_body = &pjsip_print_text_body;
msg->body = body;
}
+
+ PJ_LOG(4,(THIS_FILE, "Request %s (CSeq=%d/%.*s) created.",
+ tdata->obj_name,
+ param_cseq->cseq,
+ param_cseq->method.name.slen,
+ param_cseq->method.name.ptr));
+
}
/*
@@ -328,12 +247,6 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt,
}
PJ_END
- PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.",
- tdata->obj_name,
- cseq->cseq,
- cseq->method.name.slen,
- cseq->method.name.ptr));
-
*p_tdata = tdata;
return PJ_SUCCESS;
@@ -366,23 +279,30 @@ pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
PJ_LOG(5,(THIS_FILE, "Entering pjsip_endpt_create_request_from_hdr()"));
+ /* Check arguments. */
+ PJ_ASSERT_RETURN(endpt && method && param_target && param_from &&
+ param_to && p_tdata, PJ_EINVAL);
+
+ /* Create new transmit data. */
status = pjsip_endpt_create_tdata(endpt, &tdata);
if (status != PJ_SUCCESS)
return status;
+ /* Set initial reference counter to 1. */
pjsip_tx_data_add_ref(tdata);
PJ_TRY {
+ /* Duplicate target URI and headers. */
target = pjsip_uri_clone(tdata->pool, param_target);
- from = pjsip_hdr_shallow_clone(tdata->pool, param_from);
+ from = pjsip_hdr_clone(tdata->pool, param_from);
pjsip_fromto_set_from(from);
- to = pjsip_hdr_shallow_clone(tdata->pool, param_to);
+ to = pjsip_hdr_clone(tdata->pool, param_to);
pjsip_fromto_set_to(to);
if (param_contact)
- contact = pjsip_hdr_shallow_clone(tdata->pool, param_contact);
+ contact = pjsip_hdr_clone(tdata->pool, param_contact);
else
contact = NULL;
- call_id = pjsip_hdr_shallow_clone(tdata->pool, param_call_id);
+ call_id = pjsip_hdr_clone(tdata->pool, param_call_id);
cseq = pjsip_cseq_hdr_create(tdata->pool);
if (param_cseq >= 0)
cseq->cseq = param_cseq;
@@ -390,6 +310,7 @@ pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
cseq->cseq = pj_rand() % 0xFFFF;
pjsip_method_copy(tdata->pool, &cseq->method, method);
+ /* Copy headers to the request. */
init_request_throw(endpt, tdata, &cseq->method, target, from, to,
contact, call_id, cseq, param_text);
}
@@ -399,12 +320,6 @@ pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
}
PJ_END;
- PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.",
- tdata->obj_name,
- cseq->cseq,
- cseq->method.name.slen,
- cseq->method.name.ptr));
-
*p_tdata = tdata;
return PJ_SUCCESS;
@@ -418,7 +333,8 @@ on_error:
*/
PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
const pjsip_rx_data *rdata,
- int code,
+ int st_code,
+ const pj_str_t *st_text,
pjsip_tx_data **p_tdata)
{
pjsip_tx_data *tdata;
@@ -434,19 +350,25 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
/* Log this action. */
PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_response(rdata=%p, code=%d)",
- rdata, code));
+ rdata, st_code));
/* Create a new transmit buffer. */
status = pjsip_endpt_create_tdata( endpt, &tdata);
if (status != PJ_SUCCESS)
return status;
+ /* Set initial reference count to 1. */
+ pjsip_tx_data_add_ref(tdata);
+
/* Create new response message. */
tdata->msg = msg = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG);
/* Set status code and reason text. */
- msg->line.status.code = code;
- msg->line.status.reason = *pjsip_get_status_text(code);
+ msg->line.status.code = st_code;
+ if (st_text)
+ pj_strdup(tdata->pool, &msg->line.status.reason, st_text);
+ else
+ msg->line.status.reason = *pjsip_get_status_text(st_code);
/* Set TX data attributes. */
tdata->rx_timestamp = rdata->pkt_info.timestamp;
@@ -500,81 +422,95 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
* RFC3261). Note that the generation of ACK for 2xx response is different,
* and one must not use this function to generate such ACK.
*/
-PJ_DEF(void) pjsip_endpt_create_ack(pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- const pjsip_rx_data *rdata )
+PJ_DEF(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
+ const pjsip_tx_data *tdata,
+ const pjsip_rx_data *rdata,
+ pjsip_tx_data **ack_tdata)
{
- pjsip_msg *ack_msg, *invite_msg;
+ pjsip_tx_data *ack = NULL;
+ const pjsip_msg *invite_msg;
+ const pjsip_from_hdr *from_hdr;
+ const pjsip_to_hdr *to_hdr;
+ const pjsip_cid_hdr *cid_hdr;
+ const pjsip_cseq_hdr *cseq_hdr;
+ const pjsip_hdr *hdr;
pjsip_to_hdr *to;
- pjsip_from_hdr *from;
- pjsip_cseq_hdr *cseq;
- pjsip_hdr *hdr;
+ pj_status_t status;
- /* Make compiler happy. */
- PJ_UNUSED_ARG(endpt);
+ /* Log this action. */
+ PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_ack(rdata=%p)", rdata));
/* rdata must be a final response. */
pj_assert(rdata->msg_info.msg->type==PJSIP_RESPONSE_MSG &&
rdata->msg_info.msg->line.status.code >= 300);
- /* Log this action. */
- PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_ack(rdata=%p)", rdata));
-
- /* Create new request message. */
- ack_msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
- pjsip_method_set( &ack_msg->line.req.method, PJSIP_ACK_METHOD );
+ /* Initialize return value to NULL. */
+ *ack_tdata = NULL;
/* The original INVITE message. */
invite_msg = tdata->msg;
- /* Copy Request-Uri from the original INVITE. */
- ack_msg->line.req.uri = invite_msg->line.req.uri;
-
- /* Copy Call-ID from the original INVITE */
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_CALL_ID, NULL);
- pjsip_msg_add_hdr( ack_msg, hdr );
+ /* Get the headers from original INVITE request. */
+# define FIND_HDR(m,HNAME) pjsip_msg_find_hdr(m, PJSIP_H_##HNAME, NULL)
- /* Copy From header from the original INVITE. */
- from = (pjsip_from_hdr*)pjsip_msg_find_remove_hdr(invite_msg,
- PJSIP_H_FROM, NULL);
- pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)from );
+ from_hdr = (const pjsip_from_hdr*) FIND_HDR(invite_msg, FROM);
+ PJ_ASSERT_ON_FAIL(from_hdr != NULL, goto on_missing_hdr);
- /* Copy To header from the original INVITE. */
- to = (pjsip_to_hdr*)pjsip_msg_find_remove_hdr( invite_msg,
- PJSIP_H_TO, NULL);
- pj_strdup(tdata->pool, &to->tag, &rdata->msg_info.to->tag);
- pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)to );
+ to_hdr = (const pjsip_to_hdr*) FIND_HDR(invite_msg, TO);
+ PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
- /* Must contain single Via, just as the original INVITE. */
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_VIA, NULL);
- pjsip_msg_insert_first_hdr( ack_msg, hdr );
+ cid_hdr = (const pjsip_cid_hdr*) FIND_HDR(invite_msg, CALL_ID);
+ PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
- /* Must have the same CSeq value as the original INVITE, but method
- * changed to ACK
- */
- cseq = (pjsip_cseq_hdr*) pjsip_msg_find_remove_hdr( invite_msg,
- PJSIP_H_CSEQ, NULL);
- pjsip_method_set( &cseq->method, PJSIP_ACK_METHOD );
- pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*) cseq );
+ cseq_hdr = (const pjsip_cseq_hdr*) FIND_HDR(invite_msg, CSEQ);
+ PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
+
+# undef FIND_HDR
+
+ /* Create new request message from the headers. */
+ status = pjsip_endpt_create_request_from_hdr(endpt,
+ &pjsip_ack_method,
+ tdata->msg->line.req.uri,
+ from_hdr, to_hdr,
+ NULL, cid_hdr,
+ cseq_hdr->cseq, NULL,
+ &ack);
+
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Update tag in To header with the one from the response (if any). */
+ to = (pjsip_to_hdr*) pjsip_msg_find_hdr(ack->msg, PJSIP_H_TO, NULL);
+ pj_strdup(ack->pool, &to->tag, &rdata->msg_info.to->tag);
+
+ /* Must contain single Via, just as the original INVITE. */
+ hdr = pjsip_msg_find_hdr( invite_msg, PJSIP_H_VIA, NULL);
+ if (hdr) {
+ pjsip_msg_insert_first_hdr( ack->msg, pjsip_hdr_clone(ack->pool,hdr) );
+ }
/* If the original INVITE has Route headers, those header fields MUST
* appear in the ACK.
*/
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
+ hdr = pjsip_msg_find_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
while (hdr != NULL) {
- pjsip_msg_add_hdr( ack_msg, hdr );
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
+ pjsip_msg_add_hdr( ack->msg, pjsip_hdr_clone(ack->pool, hdr) );
+ hdr = hdr->next;
+ if (hdr == &invite_msg->hdr)
+ break;
+ hdr = pjsip_msg_find_hdr( invite_msg, PJSIP_H_ROUTE, hdr);
}
- /* Set the message in the "tdata" to point to the ACK message. */
- tdata->msg = ack_msg;
-
- /* Reset transmit packet buffer, to force 're-printing' of message. */
- tdata->buf.cur = tdata->buf.start;
-
/* We're done.
* "tdata" parameter now contains the ACK message.
*/
+ *ack_tdata = ack;
+ return PJ_SUCCESS;
+
+on_missing_hdr:
+ if (ack)
+ pjsip_tx_data_dec_ref(ack);
+ return PJSIP_EMISSINGHDR;
}
@@ -583,82 +519,74 @@ PJ_DEF(void) pjsip_endpt_create_ack(pjsip_endpoint *endpt,
* chapter 9.1 of RFC3261.
*/
PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
- pjsip_tx_data *req_tdata,
+ const pjsip_tx_data *req_tdata,
pjsip_tx_data **p_tdata)
{
- pjsip_msg *req_msg; /* the original request. */
- pjsip_tx_data *cancel_tdata;
- pjsip_msg *cancel_msg;
- pjsip_hdr *hdr;
- pjsip_cseq_hdr *req_cseq, *cseq;
- pjsip_uri *req_uri;
+ pjsip_tx_data *cancel_tdata = NULL;
+ const pjsip_from_hdr *from_hdr;
+ const pjsip_to_hdr *to_hdr;
+ const pjsip_cid_hdr *cid_hdr;
+ const pjsip_cseq_hdr *cseq_hdr;
+ const pjsip_hdr *hdr;
pj_status_t status;
/* Log this action. */
PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_cancel(tdata=%p)", req_tdata));
- /* Get the original request. */
- req_msg = req_tdata->msg;
-
/* The transmit buffer must INVITE request. */
- PJ_ASSERT_RETURN(req_msg->type == PJSIP_REQUEST_MSG &&
- req_msg->line.req.method.id == PJSIP_INVITE_METHOD,
+ PJ_ASSERT_RETURN(req_tdata->msg->type == PJSIP_REQUEST_MSG &&
+ req_tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD,
PJ_EINVAL);
- /* Create new transmit buffer. */
- status = pjsip_endpt_create_tdata( endpt, &cancel_tdata);
- if (status != PJ_SUCCESS) {
- return status;
- }
+ /* Get the headers from original INVITE request. */
+# define FIND_HDR(m,HNAME) pjsip_msg_find_hdr(m, PJSIP_H_##HNAME, NULL)
- /* Create CANCEL request message. */
- cancel_msg = pjsip_msg_create(cancel_tdata->pool, PJSIP_REQUEST_MSG);
- cancel_tdata->msg = cancel_msg;
+ from_hdr = (const pjsip_from_hdr*) FIND_HDR(req_tdata->msg, FROM);
+ PJ_ASSERT_ON_FAIL(from_hdr != NULL, goto on_missing_hdr);
- /* Request-URI, Call-ID, From, To, and the numeric part of the CSeq are
- * copied from the original request.
- */
- /* Set request line. */
- pjsip_method_set(&cancel_msg->line.req.method, PJSIP_CANCEL_METHOD);
- req_uri = req_msg->line.req.uri;
- cancel_msg->line.req.uri = pjsip_uri_clone(cancel_tdata->pool, req_uri);
+ to_hdr = (const pjsip_to_hdr*) FIND_HDR(req_tdata->msg, TO);
+ PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
- /* Copy Call-ID */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_CALL_ID, NULL);
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+ cid_hdr = (const pjsip_cid_hdr*) FIND_HDR(req_tdata->msg, CALL_ID);
+ PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
- /* Copy From header. */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_FROM, NULL);
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+ cseq_hdr = (const pjsip_cseq_hdr*) FIND_HDR(req_tdata->msg, CSEQ);
+ PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
- /* Copy To header. */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_TO, NULL);
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+# undef FIND_HDR
- /* Create new CSeq with equal number, but method set to CANCEL. */
- req_cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(req_msg, PJSIP_H_CSEQ, NULL);
- cseq = pjsip_cseq_hdr_create(cancel_tdata->pool);
- cseq->cseq = req_cseq->cseq;
- pjsip_method_set(&cseq->method, PJSIP_CANCEL_METHOD);
- pjsip_msg_add_hdr(cancel_msg, (pjsip_hdr*)cseq);
+ /* Create new request message from the headers. */
+ status = pjsip_endpt_create_request_from_hdr(endpt,
+ &pjsip_cancel_method,
+ req_tdata->msg->line.req.uri,
+ from_hdr, to_hdr,
+ NULL, cid_hdr,
+ cseq_hdr->cseq, NULL,
+ &cancel_tdata);
+
+ if (status != PJ_SUCCESS)
+ return status;
/* Must only have single Via which matches the top-most Via in the
* request being cancelled.
*/
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, NULL);
- pjsip_msg_insert_first_hdr(cancel_msg,
- pjsip_hdr_clone(cancel_tdata->pool, hdr));
+ hdr = pjsip_msg_find_hdr(req_tdata->msg, PJSIP_H_VIA, NULL);
+ if (hdr) {
+ pjsip_msg_insert_first_hdr(cancel_tdata->msg,
+ pjsip_hdr_clone(cancel_tdata->pool, hdr));
+ }
/* If the original request has Route header, the CANCEL request must also
* has exactly the same.
* Copy "Route" header from the request.
*/
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, NULL);
+ hdr = pjsip_msg_find_hdr(req_tdata->msg, PJSIP_H_ROUTE, NULL);
while (hdr != NULL) {
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+ pjsip_msg_add_hdr(cancel_tdata->msg,
+ pjsip_hdr_clone(cancel_tdata->pool, hdr));
hdr = hdr->next;
- if (hdr != &cancel_msg->hdr)
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, hdr);
+ if (hdr != &cancel_tdata->msg->hdr)
+ hdr = pjsip_msg_find_hdr(cancel_tdata->msg, PJSIP_H_ROUTE, hdr);
else
break;
}
@@ -668,51 +596,591 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
*/
*p_tdata = cancel_tdata;
return PJ_SUCCESS;
+
+on_missing_hdr:
+ if (cancel_tdata)
+ pjsip_tx_data_dec_ref(cancel_tdata);
+ return PJSIP_EMISSINGHDR;
}
-/* Get the address parameters (host, port, flag, TTL, etc) to send the
- * response.
+
+/*
+ * Find which destination to be used to send the request message, based
+ * on the request URI and Route headers in the message. The procedure
+ * used here follows the guidelines on sending the request in RFC 3261
+ * chapter 8.1.2.
*/
-PJ_DEF(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
- const pjsip_transport *req_transport,
- const pjsip_via_hdr *via,
- pjsip_host_info *send_addr)
+PJ_DEF(pj_status_t) pjsip_get_request_addr( pjsip_tx_data *tdata,
+ pjsip_host_info *dest_info )
{
- /* Determine the destination address (section 18.2.2):
- * - for TCP, SCTP, or TLS, send the response using the transport where
- * the request was received.
- * - if maddr parameter is present, send to this address using the port
- * in sent-by or 5060. If multicast is used, the TTL in the Via must
- * be used, or 1 if ttl parameter is not present.
- * - otherwise if received parameter is present, set to this address.
- * - otherwise send to the address in sent-by.
+ const pjsip_uri *new_request_uri, *target_uri;
+ const pjsip_name_addr *topmost_route_uri;
+ pjsip_route_hdr *first_route_hdr, *last_route_hdr;
+
+ PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
+ PJSIP_ENOTREQUESTMSG);
+ PJ_ASSERT_RETURN(dest_info != NULL, PJ_EINVAL);
+
+ /* Get the first "Route" header from the message. If the message doesn't
+ * have any "Route" headers but the endpoint has, then copy the "Route"
+ * headers from the endpoint first.
*/
- send_addr->flag = req_transport->flag;
- send_addr->type = req_transport->key.type;
+ last_route_hdr = first_route_hdr =
+ pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
+ if (first_route_hdr) {
+ topmost_route_uri = &first_route_hdr->name_addr;
+ while (last_route_hdr->next != (void*)&tdata->msg->hdr) {
+ pjsip_route_hdr *hdr;
+ hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE,
+ last_route_hdr->next);
+ if (!hdr)
+ break;
+ last_route_hdr = hdr;
+ }
+ } else {
+ topmost_route_uri = NULL;
+ }
- if (PJSIP_TRANSPORT_IS_RELIABLE(req_transport)) {
- pj_strdup( pool, &send_addr->addr.host,
- &req_transport->remote_name.host);
- send_addr->addr.port = req_transport->remote_name.port;
+ /* If Route headers exist, and the first element indicates loose-route,
+ * the URI is taken from the Request-URI, and we keep all existing Route
+ * headers intact.
+ * If Route headers exist, and the first element DOESN'T indicate loose
+ * route, the URI is taken from the first Route header, and remove the
+ * first Route header from the message.
+ * Otherwise if there's no Route headers, the URI is taken from the
+ * Request-URI.
+ */
+ if (topmost_route_uri) {
+ pj_bool_t has_lr_param;
+
+ if (PJSIP_URI_SCHEME_IS_SIP(topmost_route_uri) ||
+ PJSIP_URI_SCHEME_IS_SIPS(topmost_route_uri))
+ {
+ const pjsip_url *url = pjsip_uri_get_uri((void*)topmost_route_uri);
+ has_lr_param = url->lr_param;
+ } else {
+ has_lr_param = 0;
+ }
+ if (has_lr_param) {
+ new_request_uri = tdata->msg->line.req.uri;
+ /* We shouldn't need to delete topmost Route if it has lr param.
+ * But seems like it breaks some proxy implementation, so we
+ * delete it anyway.
+ */
+ /*
+ pj_list_erase(first_route_hdr);
+ if (first_route_hdr == last_route_hdr)
+ last_route_hdr = NULL;
+ */
+ } else {
+ new_request_uri = pjsip_uri_get_uri((void*)topmost_route_uri);
+ pj_list_erase(first_route_hdr);
+ if (first_route_hdr == last_route_hdr)
+ last_route_hdr = NULL;
+ }
+
+ target_uri = (pjsip_uri*)topmost_route_uri;
+
+ } else {
+ target_uri = new_request_uri = tdata->msg->line.req.uri;
+ }
+
+ /* The target URI must be a SIP/SIPS URL so we can resolve it's address.
+ * Otherwise we're in trouble (i.e. there's no host part in tel: URL).
+ */
+ pj_memset(dest_info, 0, sizeof(*dest_info));
+
+ if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) {
+ pjsip_uri *uri = (pjsip_uri*) target_uri;
+ const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
+ dest_info->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE);
+ pj_strdup(tdata->pool, &dest_info->addr.host, &url->host);
+ dest_info->addr.port = url->port;
+ dest_info->type =
+ pjsip_transport_get_type_from_name(&url->transport_param);
+
+ } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) {
+ pjsip_uri *uri = (pjsip_uri*) target_uri;
+ const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
+ pj_strdup(tdata->pool, &dest_info->addr.host, &url->host);
+ dest_info->addr.port = url->port;
+ dest_info->type =
+ pjsip_transport_get_type_from_name(&url->transport_param);
+#if PJ_HAS_TCP
+ if (dest_info->type == PJSIP_TRANSPORT_TCP ||
+ dest_info->type == PJSIP_TRANSPORT_SCTP)
+ {
+ dest_info->flag |= PJSIP_TRANSPORT_RELIABLE;
+ }
+#endif
} else {
- /* Set the host part */
- if (via->maddr_param.slen) {
- pj_strdup(pool, &send_addr->addr.host, &via->maddr_param);
- } else if (via->recvd_param.slen) {
- pj_strdup(pool, &send_addr->addr.host, &via->recvd_param);
+ pj_assert(!"Unsupported URI scheme!");
+ PJ_TODO(SUPPORT_REQUEST_ADDR_RESOLUTION_FOR_TEL_URI);
+ return PJSIP_EINVALIDSCHEME;
+ }
+
+ /* If target URI is different than request URI, replace
+ * request URI add put the original URI in the last Route header.
+ */
+ if (new_request_uri && new_request_uri!=tdata->msg->line.req.uri) {
+ pjsip_route_hdr *route = pjsip_route_hdr_create(tdata->pool);
+ route->name_addr.uri = tdata->msg->line.req.uri;
+ if (last_route_hdr)
+ pj_list_insert_after(last_route_hdr, route);
+ else
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route);
+ tdata->msg->line.req.uri = (pjsip_uri*)new_request_uri;
+ }
+
+ /* Success. */
+ return PJ_SUCCESS;
+}
+
+
+/* Transport callback for sending stateless request.
+ * This is one of the most bizzare function in pjsip, so
+ * good luck if you happen to debug this function!!
+ */
+static void stateless_send_transport_cb( void *token,
+ pjsip_tx_data *tdata,
+ pj_ssize_t sent )
+{
+ pjsip_send_state *stateless_data = token;
+
+ PJ_UNUSED_ARG(tdata);
+ pj_assert(tdata == stateless_data->tdata);
+
+ for (;;) {
+ pj_status_t status;
+ pj_bool_t cont;
+
+ pj_sockaddr_t *cur_addr;
+ pjsip_transport_type_e cur_addr_type;
+ int cur_addr_len;
+
+ pjsip_via_hdr *via;
+
+ if (sent == -PJ_EPENDING) {
+ /* This is the initial process.
+ * When the process started, this function will be called by
+ * stateless_send_resolver_callback() with sent argument set to
+ * -PJ_EPENDING.
+ */
+ cont = PJ_TRUE;
+ } else {
+ /* There are two conditions here:
+ * (1) Message is sent (i.e. sent > 0),
+ * (2) Failure (i.e. sent <= 0)
+ */
+ cont = (sent > 0) ? PJ_FALSE :
+ (stateless_data->cur_addr<stateless_data->addr.count-1);
+ if (stateless_data->app_cb) {
+ (*stateless_data->app_cb)(stateless_data, sent, &cont);
+ } else {
+ /* Doesn't have application callback.
+ * Terminate the process.
+ */
+ cont = PJ_FALSE;
+ }
+ }
+
+ /* Finished with this transport. */
+ if (stateless_data->cur_transport) {
+ pjsip_transport_dec_ref(stateless_data->cur_transport);
+ stateless_data->cur_transport = NULL;
+ }
+
+ /* Done if application doesn't want to continue. */
+ if (sent > 0 || !cont) {
+ pjsip_tx_data_dec_ref(tdata);
+ return;
+ }
+
+ /* Try next address, if any, and only when this is not the
+ * first invocation.
+ */
+ if (sent != -PJ_EPENDING) {
+ stateless_data->cur_addr++;
+ }
+
+ /* Have next address? */
+ if (stateless_data->cur_addr >= stateless_data->addr.count) {
+ /* This only happens when a rather buggy application has
+ * sent 'cont' to PJ_TRUE when the initial value was PJ_FALSE.
+ * In this case just stop the processing; we don't need to
+ * call the callback again as application has been informed
+ * before.
+ */
+ pjsip_tx_data_dec_ref(tdata);
+ return;
+ }
+
+ /* Keep current server address information handy. */
+ cur_addr = &stateless_data->addr.entry[stateless_data->cur_addr].addr;
+ cur_addr_type = stateless_data->addr.entry[stateless_data->cur_addr].type;
+ cur_addr_len = stateless_data->addr.entry[stateless_data->cur_addr].addr_len;
+
+ /* Acquire transport. */
+ status = pjsip_endpt_acquire_transport( stateless_data->endpt,
+ cur_addr_type,
+ cur_addr,
+ cur_addr_len,
+ &stateless_data->cur_transport);
+ if (status != PJ_SUCCESS) {
+ sent = -status;
+ continue;
+ }
+
+ /* Modify Via header. */
+ via = (pjsip_via_hdr*) pjsip_msg_find_hdr( tdata->msg,
+ PJSIP_H_VIA, NULL);
+ if (!via) {
+ /* Shouldn't happen if request was created with PJSIP API!
+ * But we handle the case anyway for robustness.
+ */
+ pj_assert(!"Via header not found!");
+ via = pjsip_via_hdr_create(tdata->pool);
+ pjsip_msg_insert_first_hdr(tdata->msg, (pjsip_hdr*)via);
+ }
+
+ if (via->branch_param.slen == 0) {
+ pj_str_t tmp;
+ via->branch_param.ptr = pj_pool_alloc(tdata->pool,
+ PJSIP_MAX_BRANCH_LEN);
+ via->branch_param.slen = PJSIP_MAX_BRANCH_LEN;
+ pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID,
+ PJSIP_RFC3261_BRANCH_LEN);
+ tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN;
+ pj_generate_unique_string(&tmp);
+ }
+
+ via->transport = pj_str(stateless_data->cur_transport->type_name);
+ via->sent_by = stateless_data->cur_transport->local_name;
+ via->rport_param = 0;
+
+ /* Send message using this transport. */
+ status = pjsip_transport_send( stateless_data->cur_transport,
+ tdata,
+ cur_addr,
+ cur_addr_len,
+ stateless_data,
+ &stateless_send_transport_cb);
+ if (status == PJ_SUCCESS) {
+ /* Recursively call this function. */
+ sent = tdata->buf.cur - tdata->buf.start;
+ stateless_send_transport_cb( stateless_data, tdata, sent );
+ return;
+ } else if (status == PJ_EPENDING) {
+ /* This callback will be called later. */
+ return;
} else {
- pj_strdup(pool, &send_addr->addr.host, &via->sent_by.host);
+ /* Recursively call this function. */
+ sent = -status;
+ stateless_send_transport_cb( stateless_data, tdata, sent );
+ return;
+ }
+ }
+
+}
+
+/* Resolver callback for sending stateless request. */
+static void
+stateless_send_resolver_callback( pj_status_t status,
+ void *token,
+ const struct pjsip_server_addresses *addr)
+{
+ pjsip_send_state *stateless_data = token;
+
+ /* Fail on server resolution. */
+ if (status != PJ_SUCCESS) {
+ if (stateless_data->app_cb) {
+ pj_bool_t cont = PJ_FALSE;
+ (*stateless_data->app_cb)(stateless_data, -status, &cont);
+ }
+ pjsip_tx_data_dec_ref(stateless_data->tdata);
+ return;
+ }
+
+ /* Copy server addresses */
+ pj_memcpy( &stateless_data->addr, addr, sizeof(pjsip_server_addresses));
+
+ /* Process the addresses. */
+ stateless_send_transport_cb( stateless_data, stateless_data->tdata,
+ -PJ_EPENDING);
+}
+
+/*
+ * Send stateless request.
+ * The sending process consists of several stages:
+ * - determine which host to contact (#pjsip_get_request_addr).
+ * - resolve the host (#pjsip_endpt_resolve)
+ * - establish transport (#pjsip_endpt_acquire_transport)
+ * - send the message (#pjsip_transport_send)
+ */
+PJ_DEF(pj_status_t)
+pjsip_endpt_send_request_stateless(pjsip_endpoint *endpt,
+ pjsip_tx_data *tdata,
+ void *token,
+ void (*cb)(pjsip_send_state*,
+ pj_ssize_t sent,
+ pj_bool_t *cont))
+{
+ pjsip_host_info dest_info;
+ pjsip_send_state *stateless_data;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(endpt && tdata, PJ_EINVAL);
+
+ /* Get destination name to contact. */
+ status = pjsip_get_request_addr(tdata, &dest_info);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Keep stateless data. */
+ stateless_data = pj_pool_zalloc(tdata->pool, sizeof(pjsip_send_state));
+ stateless_data->token = token;
+ stateless_data->endpt = endpt;
+ stateless_data->tdata = tdata;
+ stateless_data->app_cb = cb;
+
+ /* Resolve destination host.
+ * The processing then resumed when the resolving callback is called.
+ */
+ pjsip_endpt_resolve( endpt, tdata->pool, &dest_info, stateless_data,
+ &stateless_send_resolver_callback);
+ return PJ_SUCCESS;
+}
+
+/*
+ * Determine which address (and transport) to use to send response message
+ * based on the received request. This function follows the specification
+ * in section 18.2.2 of RFC 3261 and RFC 3581 for calculating the destination
+ * address and transport.
+ */
+PJ_DEF(pj_status_t) pjsip_get_response_addr( pj_pool_t *pool,
+ pjsip_rx_data *rdata,
+ pjsip_response_addr *res_addr )
+{
+ pjsip_transport *src_transport = rdata->tp_info.transport;
+
+ /* Check arguments. */
+ PJ_ASSERT_RETURN(pool && rdata && res_addr, PJ_EINVAL);
+
+ /* All requests must have "received" parameter.
+ * This must always be done in transport layer.
+ */
+ pj_assert(rdata->msg_info.via->recvd_param.slen != 0);
+
+ /* Do the calculation based on RFC 3261 Section 18.2.2 and RFC 3581 */
+
+ if (PJSIP_TRANSPORT_IS_RELIABLE(src_transport)) {
+ /* For reliable protocol such as TCP or SCTP, or TLS over those, the
+ * response MUST be sent using the existing connection to the source
+ * of the original request that created the transaction, if that
+ * connection is still open.
+ * If that connection is no longer open, the server SHOULD open a
+ * connection to the IP address in the received parameter, if present,
+ * using the port in the sent-by value, or the default port for that
+ * transport, if no port is specified.
+ * If that connection attempt fails, the server SHOULD use the
+ * procedures in [4] for servers in order to determine the IP address
+ * and port to open the connection and send the response to.
+ */
+ res_addr->transport = rdata->tp_info.transport;
+ pj_memcpy(&res_addr->addr, &rdata->pkt_info.src_addr,
+ rdata->pkt_info.src_addr_len);
+ res_addr->addr_len = rdata->pkt_info.src_addr_len;
+ res_addr->dst_host.type = src_transport->key.type;
+ res_addr->dst_host.flag = src_transport->flag;
+ pj_strdup( pool, &res_addr->dst_host.addr.host,
+ &rdata->msg_info.via->recvd_param);
+ res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port;
+ if (res_addr->dst_host.addr.port == 0) {
+ res_addr->dst_host.addr.port =
+ pjsip_transport_get_default_port_for_type(res_addr->dst_host.type);
+ }
+
+ } else if (rdata->msg_info.via->maddr_param.slen) {
+ /* Otherwise, if the Via header field value contains a maddr parameter,
+ * the response MUST be forwarded to the address listed there, using
+ * the port indicated in sent-by, or port 5060 if none is present.
+ * If the address is a multicast address, the response SHOULD be sent
+ * using the TTL indicated in the ttl parameter, or with a TTL of 1 if
+ * that parameter is not present.
+ */
+ res_addr->transport = NULL;
+ res_addr->dst_host.type = src_transport->key.type;
+ res_addr->dst_host.flag = src_transport->flag;
+ pj_strdup( pool, &res_addr->dst_host.addr.host,
+ &rdata->msg_info.via->maddr_param);
+ res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port;
+ if (res_addr->dst_host.addr.port == 0)
+ res_addr->dst_host.addr.port = 5060;
+
+ } else if (rdata->msg_info.via->rport_param >= 0) {
+ /* There is both a "received" parameter and an "rport" parameter,
+ * the response MUST be sent to the IP address listed in the "received"
+ * parameter, and the port in the "rport" parameter.
+ * The response MUST be sent from the same address and port that the
+ * corresponding request was received on.
+ */
+ res_addr->transport = rdata->tp_info.transport;
+ pj_memcpy(&res_addr->addr, &rdata->pkt_info.src_addr,
+ rdata->pkt_info.src_addr_len);
+ res_addr->addr_len = rdata->pkt_info.src_addr_len;
+ res_addr->dst_host.type = src_transport->key.type;
+ res_addr->dst_host.flag = src_transport->flag;
+ pj_strdup( pool, &res_addr->dst_host.addr.host,
+ &rdata->msg_info.via->recvd_param);
+ res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port;
+ if (res_addr->dst_host.addr.port == 0) {
+ res_addr->dst_host.addr.port =
+ pjsip_transport_get_default_port_for_type(res_addr->dst_host.type);
}
- /* Set the port */
- send_addr->addr.port = via->sent_by.port;
+ } else {
+ res_addr->transport = NULL;
+ res_addr->dst_host.type = src_transport->key.type;
+ res_addr->dst_host.flag = src_transport->flag;
+ pj_strdup( pool, &res_addr->dst_host.addr.host,
+ &rdata->msg_info.via->recvd_param);
+ res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port;
+ if (res_addr->dst_host.addr.port == 0) {
+ res_addr->dst_host.addr.port =
+ pjsip_transport_get_default_port_for_type(res_addr->dst_host.type);
+ }
}
return PJ_SUCCESS;
}
/*
+ * Callback called by transport during send_response.
+ */
+static void send_response_transport_cb(void *token, pjsip_tx_data *tdata,
+ pj_ssize_t sent)
+{
+ pjsip_send_state *send_state = token;
+ pj_bool_t cont = PJ_FALSE;
+
+ /* Call callback, if any. */
+ if (send_state->app_cb)
+ (*send_state->app_cb)(send_state, sent, &cont);
+
+ /* Decrement transport reference counter. */
+ pjsip_transport_dec_ref(send_state->cur_transport);
+
+ /* Decrement transmit data ref counter. */
+ pjsip_tx_data_dec_ref(tdata);
+}
+
+/*
+ * Resolver calback during send_response.
+ */
+static void send_response_resolver_cb( pj_status_t status, void *token,
+ const pjsip_server_addresses *addr )
+{
+ pjsip_send_state *send_state = token;
+
+ if (status != PJ_SUCCESS) {
+ if (send_state->app_cb) {
+ pj_bool_t cont = PJ_FALSE;
+ (*send_state->app_cb)(send_state, -status, &cont);
+ }
+ pjsip_tx_data_dec_ref(send_state->tdata);
+ return;
+ }
+
+ /* Only handle the first address resolved. */
+
+ /* Acquire transport. */
+ status = pjsip_endpt_acquire_transport( send_state->endpt,
+ addr->entry[0].type,
+ &addr->entry[0].addr,
+ addr->entry[0].addr_len,
+ &send_state->cur_transport);
+ if (status != PJ_SUCCESS) {
+ if (send_state->app_cb) {
+ pj_bool_t cont = PJ_FALSE;
+ (*send_state->app_cb)(send_state, -status, &cont);
+ }
+ pjsip_tx_data_dec_ref(send_state->tdata);
+ return;
+ }
+
+ /* Send response using the transoprt. */
+ status = pjsip_transport_send( send_state->cur_transport,
+ send_state->tdata,
+ &addr->entry[0].addr,
+ addr->entry[0].addr_len,
+ send_state,
+ &send_response_transport_cb);
+ if (status == PJ_SUCCESS) {
+ pj_ssize_t sent = send_state->tdata->buf.cur -
+ send_state->tdata->buf.start;
+ send_response_transport_cb(send_state, send_state->tdata, sent);
+
+ } else if (status == PJ_EPENDING) {
+ /* Transport callback will be called later. */
+ } else {
+ send_response_transport_cb(send_state, send_state->tdata, -status);
+ }
+}
+
+/*
+ * Send response.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_send_response( pjsip_endpoint *endpt,
+ pjsip_response_addr *res_addr,
+ pjsip_tx_data *tdata,
+ void *token,
+ void (*cb)(pjsip_send_state*,
+ pj_ssize_t sent,
+ pj_bool_t *cont))
+{
+ /* Determine which transports and addresses to send the response,
+ * based on Section 18.2.2 of RFC 3261.
+ */
+ pjsip_send_state *send_state;
+ pj_status_t status;
+
+ /* Create structure to keep the sending state. */
+ send_state = pj_pool_zalloc(tdata->pool, sizeof(pjsip_send_state));
+ send_state->endpt = endpt;
+ send_state->tdata = tdata;
+ send_state->token = token;
+ send_state->app_cb = cb;
+
+ if (res_addr->transport != NULL) {
+ send_state->cur_transport = res_addr->transport;
+ pjsip_transport_add_ref(send_state->cur_transport);
+
+ status = pjsip_transport_send( send_state->cur_transport, tdata,
+ &res_addr->addr,
+ res_addr->addr_len,
+ send_state,
+ &send_response_transport_cb );
+ if (status == PJ_SUCCESS) {
+ pj_ssize_t sent = tdata->buf.cur - tdata->buf.start;
+ send_response_transport_cb(send_state, tdata, sent);
+ return PJ_SUCCESS;
+ } else if (status == PJ_EPENDING) {
+ /* Callback will be called later. */
+ return PJ_SUCCESS;
+ } else {
+ send_response_transport_cb(send_state, tdata, -status);
+ return status;
+ }
+ } else {
+ pjsip_endpt_resolve(endpt, tdata->pool, &res_addr->dst_host,
+ send_state, &send_response_resolver_cb);
+ return PJ_SUCCESS;
+ }
+}
+
+
+/*
* Get the event string from the event ID.
*/
PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e)
diff --git a/pjsip/src/pjsip/sip_util_statefull.c b/pjsip/src/pjsip/sip_util_statefull.c
new file mode 100644
index 00000000..41efcddc
--- /dev/null
+++ b/pjsip/src/pjsip/sip_util_statefull.c
@@ -0,0 +1,108 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip/sip_util.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_event.h>
+#include <pj/pool.h>
+
+struct aux_tsx_data
+{
+ void *token;
+ void (*cb)(void*,pjsip_event*);
+};
+
+static void aux_tsx_handler( pjsip_transaction *tsx, pjsip_event *event );
+
+pjsip_module aux_tsx_module =
+{
+ NULL, NULL, /* prev and next */
+ { "Aux-Tsx", 7}, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */
+ NULL, /* User data. */
+ 0, /* Number of methods supported (=0). */
+ { 0 }, /* Array of methods (none) */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ NULL, /* on_rx_request() */
+ NULL, /* on_rx_response() */
+ &aux_tsx_handler, /* tsx_handler() */
+};
+
+static void aux_tsx_handler( pjsip_transaction *tsx, pjsip_event *event )
+{
+ struct aux_tsx_data *tsx_data;
+
+ if (event->type != PJSIP_EVENT_TSX_STATE)
+ return;
+ if (tsx->module_data[aux_tsx_module.id] == NULL)
+ return;
+ if (tsx->status_code < 200)
+ return;
+
+ /* Call the callback, if any, and prevent the callback to be called again
+ * by clearing the transaction's module_data.
+ */
+ tsx_data = tsx->module_data[aux_tsx_module.id];
+ tsx->module_data[aux_tsx_module.id] = NULL;
+
+ if (tsx_data->cb) {
+ (*tsx_data->cb)(tsx_data->token, event);
+ }
+}
+
+
+PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt,
+ pjsip_tx_data *tdata,
+ int timeout,
+ void *token,
+ void (*cb)(void*,pjsip_event*))
+{
+ pjsip_transaction *tsx;
+ struct aux_tsx_data *tsx_data;
+ pj_status_t status;
+
+ status = pjsip_endpt_create_tsx(endpt, &tsx);
+ if (!tsx) {
+ pjsip_tx_data_dec_ref(tdata);
+ return -1;
+ }
+
+ tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data));
+ tsx_data->token = token;
+ tsx_data->cb = cb;
+ tsx->module_data[aux_tsx_module.id] = tsx_data;
+
+ if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
+ pjsip_endpt_destroy_tsx(endpt, tsx);
+ pjsip_tx_data_dec_ref(tdata);
+ return -1;
+ }
+
+ pjsip_endpt_register_tsx(endpt, tsx);
+ pjsip_tx_data_invalidate_msg(tdata);
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ pjsip_tx_data_dec_ref(tdata);
+ return 0;
+}
+
diff --git a/pjsip/src/test-pjsip/main.c b/pjsip/src/test-pjsip/main.c
index 3e5270b1..3c5f0800 100644
--- a/pjsip/src/test-pjsip/main.c
+++ b/pjsip/src/test-pjsip/main.c
@@ -17,8 +17,17 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "test.h"
+#include <stdio.h>
-int main(void)
+int main(int argc, char *argv[])
{
- return test_main();
+ int retval = test_main();
+
+ if (argc != 1) {
+ char s[10];
+ printf("<Press ENTER to quit>\n");
+ fgets(s, sizeof(s), stdin);
+ }
+
+ return retval;
}
diff --git a/pjsip/src/test-pjsip/msg_test.c b/pjsip/src/test-pjsip/msg_test.c
index 1f6affdd..e3702713 100644
--- a/pjsip/src/test-pjsip/msg_test.c
+++ b/pjsip/src/test-pjsip/msg_test.c
@@ -664,7 +664,7 @@ static pjsip_msg *create_msg1(pj_pool_t *pool)
/*****************************************************************************/
-pj_status_t msg_test(void)
+int msg_test(void)
{
pj_status_t status;
pj_pool_t *pool;
diff --git a/pjsip/src/test-pjsip/test.c b/pjsip/src/test-pjsip/test.c
index 92e0c33a..a8f339c8 100644
--- a/pjsip/src/test-pjsip/test.c
+++ b/pjsip/src/test-pjsip/test.c
@@ -83,6 +83,8 @@ int test_main(void)
DO_TEST(uri_test());
DO_TEST(msg_test());
+ DO_TEST(txdata_test());
+ DO_TEST(transport_udp_test());
on_return:
diff --git a/pjsip/src/test-pjsip/test.h b/pjsip/src/test-pjsip/test.h
index aebf6022..686dea07 100644
--- a/pjsip/src/test-pjsip/test.h
+++ b/pjsip/src/test-pjsip/test.h
@@ -23,10 +23,27 @@
extern pjsip_endpoint *endpt;
-pj_status_t uri_test(void);
-pj_status_t msg_test(void);
+#define TEST_UDP_PORT 15060
+/* The tests */
+int uri_test(void);
+int msg_test(void);
+int txdata_test(void);
+int transport_udp_test(void);
+
+/* Transport test helpers (transport_test.c). */
+int generic_transport_test(pjsip_transport *tp);
+int transport_send_recv_test( pjsip_transport_type_e tp_type,
+ pjsip_transport *ref_tp,
+ const pj_sockaddr_in *rem_addr );
+int transport_rt_test( pjsip_transport_type_e tp_type,
+ pjsip_transport *ref_tp,
+ const pj_sockaddr_in *rem_addr );
+
+/* Test main entry */
int test_main(void);
+
+/* Test utilities. */
void app_perror(const char *msg, pj_status_t status);
diff --git a/pjsip/src/test-pjsip/transport_test.c b/pjsip/src/test-pjsip/transport_test.c
new file mode 100644
index 00000000..0d54d42d
--- /dev/null
+++ b/pjsip/src/test-pjsip/transport_test.c
@@ -0,0 +1,561 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "test.h"
+#include <pjsip_core.h>
+#include <pjlib.h>
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Generic testing for transport, to make sure that basic
+ * attributes have been initialized properly.
+ */
+int generic_transport_test(pjsip_transport *tp)
+{
+ PJ_LOG(3,("", " structure test..."));
+
+ /* Check that local address name is valid. */
+ {
+ struct pj_in_addr addr;
+
+ /* Note: inet_aton() returns non-zero if addr is valid! */
+ if (pj_inet_aton(&tp->local_name.host, &addr) != 0) {
+ if (addr.s_addr==PJ_INADDR_ANY || addr.s_addr==PJ_INADDR_NONE) {
+ PJ_LOG(3,("", " Error: invalid address name"));
+ return -420;
+ }
+ } else {
+ /* It's okay. local_name.host may be a hostname instead of
+ * IP address.
+ */
+ }
+ }
+
+ /* Check that port is valid. */
+ if (tp->local_name.port <= 0) {
+ return -430;
+ }
+
+ /* Check length of address (for now we only check against sockaddr_in). */
+ if (tp->addr_len != sizeof(pj_sockaddr_in))
+ return -440;
+
+ /* Check type. */
+ if (tp->key.type == PJSIP_TRANSPORT_UNSPECIFIED)
+ return -450;
+
+ /* That's it. */
+ return PJ_SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Send/receive test.
+ *
+ * This test sends a request to loopback address; as soon as request is
+ * received, response will be sent, and time is recorded.
+ *
+ * The main purpose is to test that the basic transport functionalities works,
+ * before we continue with more complicated tests.
+ */
+#define FROM_HDR "Bob <sip:bob@example.com>"
+#define TO_HDR "Alice <sip:alice@example.com>"
+#define CONTACT_HDR "Bob <sip:bob@127.0.0.1>"
+#define CALL_ID_HDR "SendRecv-Test"
+#define CSEQ_VALUE 100
+#define BODY "Hello World!"
+
+static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata);
+static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata);
+
+/* Flag to indicate message has been received
+ * (or failed to send)
+ */
+#define NO_STATUS -2
+static int send_status = NO_STATUS;
+static int recv_status = NO_STATUS;
+static pj_timestamp my_send_time, my_recv_time;
+
+/* Module to receive messages for this test. */
+static pjsip_module my_module =
+{
+ NULL, NULL, /* prev and next */
+ { "Transport-Test", 14}, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_TSX_LAYER-1, /* Priority */
+ NULL, /* User data. */
+ 0, /* Number of methods supported (=0). */
+ { 0 }, /* Array of methods (none) */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ &my_on_rx_request, /* on_rx_request() */
+ &my_on_rx_response, /* on_rx_response() */
+ NULL, /* tsx_handler() */
+};
+
+
+static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata)
+{
+ /* Check that this is our request. */
+ if (pj_strcmp2(&rdata->msg_info.call_id, CALL_ID_HDR) == 0) {
+ /* It is! */
+ /* Send response. */
+ pjsip_tx_data *tdata;
+ pjsip_response_addr res_addr;
+ pj_status_t status;
+
+ PJ_LOG(4,("test", "Received %d bytes request: --begin-\n"
+ "%s\n"
+ "--end--",
+ rdata->msg_info.len,
+ rdata->msg_info.msg_buf));
+
+
+ status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata);
+ if (status != PJ_SUCCESS) {
+ recv_status = status;
+ return PJ_TRUE;
+ }
+ status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr);
+ if (status != PJ_SUCCESS) {
+ recv_status = status;
+ pjsip_tx_data_dec_ref(tdata);
+ return PJ_TRUE;
+ }
+ status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL);
+ if (status != PJ_SUCCESS) {
+ recv_status = status;
+ pjsip_tx_data_dec_ref(tdata);
+ return PJ_TRUE;
+ }
+ return PJ_TRUE;
+ }
+
+ /* Not ours. */
+ return PJ_FALSE;
+}
+
+static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata)
+{
+ if (pj_strcmp2(&rdata->msg_info.call_id, CALL_ID_HDR) == 0) {
+ PJ_LOG(4,("test", "Received %d bytes response: --begin-\n"
+ "%s\n"
+ "--end--",
+ rdata->msg_info.len,
+ rdata->msg_info.msg_buf));
+
+ pj_get_timestamp(&my_recv_time);
+ recv_status = PJ_SUCCESS;
+ return PJ_TRUE;
+ }
+ return PJ_FALSE;
+}
+
+/* Transport callback. */
+static void send_msg_callback(pjsip_send_state *stateless_data,
+ pj_ssize_t sent, pj_bool_t *cont)
+{
+ if (sent < 1) {
+ /* Obtain the error code. */
+ send_status = -sent;
+ } else {
+ send_status = PJ_SUCCESS;
+ }
+
+ /* Don't want to continue. */
+ *cont = PJ_FALSE;
+}
+
+
+/* Test that we receive loopback message. */
+int transport_send_recv_test( pjsip_transport_type_e tp_type,
+ pjsip_transport *ref_tp,
+ const pj_sockaddr_in *rem_addr )
+{
+ pj_status_t status;
+ char target_buf[80];
+ pj_str_t target, from, to, contact, call_id, body;
+ pjsip_method method;
+ pjsip_tx_data *tdata;
+ pj_time_val timeout;
+
+ PJ_LOG(3,("", " single message round-trip test..."));
+
+ /* Register out test module to receive the message (if necessary). */
+ if (my_module.id == -1) {
+ status = pjsip_endpt_register_module( endpt, &my_module );
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to register module", status);
+ return -500;
+ }
+ }
+
+ /* Create a request message. */
+ pj_sprintf(target_buf, "sip:%s:%d", pj_inet_ntoa(rem_addr->sin_addr),
+ pj_ntohs(rem_addr->sin_port));
+ target = pj_str(target_buf);
+ from = pj_str(FROM_HDR);
+ to = pj_str(TO_HDR);
+ contact = pj_str(CONTACT_HDR);
+ call_id = pj_str(CALL_ID_HDR);
+ body = pj_str(BODY);
+
+ pjsip_method_set(&method, PJSIP_OPTIONS_METHOD);
+ status = pjsip_endpt_create_request( endpt, &method, &target, &from, &to,
+ &contact, &call_id, CSEQ_VALUE,
+ &body, &tdata );
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create request", status);
+ return -510;
+ }
+
+ /* Reset statuses */
+ send_status = recv_status = NO_STATUS;
+
+ /* Start time. */
+ pj_get_timestamp(&my_send_time);
+
+ /* Send the message (statelessly). */
+ status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL,
+ &send_msg_callback);
+ if (status != PJ_SUCCESS) {
+ /* Immediate error! */
+ pjsip_tx_data_dec_ref(tdata);
+ send_status = status;
+ }
+
+ /* Set the timeout (1 second from now) */
+ pj_gettimeofday(&timeout);
+ timeout.sec += 1;
+
+ /* Loop handling events until we get status */
+ do {
+ pj_time_val now;
+ pj_time_val poll_interval = { 0, 10 };
+
+ pj_gettimeofday(&now);
+ if (PJ_TIME_VAL_GTE(now, timeout)) {
+ PJ_LOG(3,("", " error: timeout in send/recv test"));
+ status = -540;
+ goto on_return;
+ }
+
+ if (send_status!=NO_STATUS && send_status!=PJ_SUCCESS) {
+ app_perror(" error sending message", send_status);
+ status = -550;
+ goto on_return;
+ }
+
+ if (recv_status!=NO_STATUS && recv_status!=PJ_SUCCESS) {
+ app_perror(" error receiving message", recv_status);
+ status = -560;
+ goto on_return;
+ }
+
+ if (send_status!=NO_STATUS && recv_status!=NO_STATUS) {
+ /* Success! */
+ break;
+ }
+
+ pjsip_endpt_handle_events(endpt, &poll_interval);
+
+ } while (1);
+
+ if (status == PJ_SUCCESS) {
+ unsigned usec_rt;
+ usec_rt = pj_elapsed_usec(&my_send_time, &my_recv_time);
+ PJ_LOG(3,("", " round-trip = %d usec", usec_rt));
+ }
+
+ status = PJ_SUCCESS;
+
+on_return:
+ return status;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Multithreaded round-trip test
+ *
+ * This test will spawn multiple threads, each of them send a request. As soon
+ * as request is received, response will be sent, and time is recorded.
+ *
+ * The main purpose of this test is to ensure there's no crash when multiple
+ * threads are sending/receiving messages.
+ *
+ */
+static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata);
+static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata);
+
+static pjsip_module rt_module =
+{
+ NULL, NULL, /* prev and next */
+ { "Transport-RT-Test", 17}, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_TSX_LAYER-1, /* Priority */
+ NULL, /* User data. */
+ 0, /* Number of methods supported (=0). */
+ { 0 }, /* Array of methods (none) */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ &rt_on_rx_request, /* on_rx_request() */
+ &rt_on_rx_response, /* on_rx_response() */
+ NULL, /* tsx_handler() */
+};
+
+static struct
+{
+ pj_thread_t *thread;
+ pj_timestamp send_time;
+ pj_timestamp total_rt_time;
+ int sent_request_count, recv_response_count;
+ pj_str_t call_id;
+} rt_test_data[16];
+
+static char rt_target_uri[32];
+static pj_bool_t rt_stop;
+static pj_str_t rt_call_id;
+
+static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata)
+{
+ if (!pj_strncmp(&rdata->msg_info.call_id, &rt_call_id, rt_call_id.slen)) {
+ char *pos = pj_strchr(&rdata->msg_info.call_id, '/');
+ int thread_id = (*pos - '0');
+
+ pjsip_tx_data *tdata;
+ pjsip_response_addr res_addr;
+ pj_status_t status;
+
+ status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata);
+ if (status != PJ_SUCCESS) {
+ return PJ_TRUE;
+ }
+ status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr);
+ if (status != PJ_SUCCESS) {
+ pjsip_tx_data_dec_ref(tdata);
+ return PJ_TRUE;
+ }
+ status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL);
+ if (status != PJ_SUCCESS) {
+ pjsip_tx_data_dec_ref(tdata);
+ return PJ_TRUE;
+ }
+ return PJ_TRUE;
+
+ }
+ return PJ_FALSE;
+}
+
+static pj_status_t rt_send_request(int thread_id)
+{
+ pj_status_t status;
+ pj_str_t target, from, to, contact, call_id;
+ pjsip_tx_data *tdata;
+
+ /* Create a request message. */
+ target = pj_str(rt_target_uri);
+ from = pj_str(FROM_HDR);
+ to = pj_str(TO_HDR);
+ contact = pj_str(CONTACT_HDR);
+ call_id = rt_test_data[thread_id].call_id;
+
+ status = pjsip_endpt_create_request( endpt, &pjsip_options_method,
+ &target, &from, &to,
+ &contact, &call_id, -1,
+ NULL, &tdata );
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create request", status);
+ return -610;
+ }
+
+ /* Start time. */
+ pj_get_timestamp(&rt_test_data[thread_id].send_time);
+
+ /* Send the message (statelessly). */
+ status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL, NULL);
+ if (status != PJ_SUCCESS) {
+ /* Immediate error! */
+ app_perror(" error: send request", status);
+ pjsip_tx_data_dec_ref(tdata);
+ return -620;
+ }
+
+ /* Update counter. */
+ rt_test_data[thread_id].sent_request_count++;
+
+ return PJ_SUCCESS;
+}
+
+static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata)
+{
+ if (!pj_strncmp(&rdata->msg_info.call_id, &rt_call_id, rt_call_id.slen)) {
+ char *pos = pj_strchr(&rdata->msg_info.call_id, '/')+1;
+ int thread_id = (*pos - '0');
+ pj_timestamp recv_time;
+
+ /* Update counter and end-time. */
+ rt_test_data[thread_id].recv_response_count++;
+ pj_get_timestamp(&recv_time);
+
+ pj_sub_timestamp(&recv_time, &rt_test_data[thread_id].send_time);
+ pj_add_timestamp(&rt_test_data[thread_id].total_rt_time, &recv_time);
+
+ if (!rt_stop)
+ rt_send_request(thread_id);
+ return PJ_TRUE;
+ }
+ return PJ_FALSE;
+}
+
+static int rt_thread(void *arg)
+{
+ int thread_id = (int)arg;
+ pj_time_val poll_delay = { 0, 10 };
+
+ /* Sleep to allow main threads to run. */
+ pj_thread_sleep(10);
+
+ /* Send the first request. */
+ if (rt_send_request(thread_id) != PJ_SUCCESS)
+ return -1;
+
+ while (!rt_stop) {
+ pjsip_endpt_handle_events(endpt, &poll_delay);
+ }
+ return 0;
+}
+
+int transport_rt_test( pjsip_transport_type_e tp_type,
+ pjsip_transport *ref_tp,
+ const pj_sockaddr_in *rem_addr )
+{
+ enum { THREADS = 4, INTERVAL = 10 };
+ int i;
+ pj_status_t status;
+ pj_pool_t *pool;
+ pj_bool_t is_reliable;
+
+ pj_timestamp zero_time, total_time;
+ unsigned usec_rt;
+ unsigned total_sent;
+ unsigned total_recv;
+
+
+ PJ_LOG(3,("", " multithreaded round-trip test (%d threads)...",
+ THREADS));
+ PJ_LOG(3,("", " this will take approx %d seconds, please wait..", INTERVAL));
+
+ is_reliable = (pjsip_transport_get_flag_from_type(tp_type) & PJSIP_TRANSPORT_RELIABLE);
+
+ /* Register module (if not yet registered) */
+ if (rt_module.id == -1) {
+ status = pjsip_endpt_register_module( endpt, &rt_module );
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to register module", status);
+ return -600;
+ }
+ }
+
+ /* Create pool for this test. */
+ pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000);
+ if (!pool)
+ return -610;
+
+ /* Initialize static test data. */
+ pj_sprintf(rt_target_uri, "sip:%s:%d", pj_inet_ntoa(rem_addr->sin_addr),
+ pj_ntohs(rem_addr->sin_port));
+ rt_call_id = pj_str("RT-Call-Id/");
+ rt_stop = PJ_FALSE;
+
+ /* Initialize thread data. */
+ for (i=0; i<THREADS; ++i) {
+ char buf[1];
+ pj_str_t str_id = { buf, 1 };
+
+ pj_memset(&rt_test_data[i], 0, sizeof(rt_test_data[i]));
+
+ /* Generate Call-ID for each thread. */
+ rt_test_data[i].call_id.ptr = pj_pool_alloc(pool, rt_call_id.slen+1);
+ pj_strcpy(&rt_test_data[i].call_id, &rt_call_id);
+ buf[0] = '0' + i;
+ pj_strcat(&rt_test_data[i].call_id, &str_id);
+
+ /* Create thread, suspended. */
+ status = pj_thread_create(pool, "rttest%p", &rt_thread, (void*)i, 0,
+ PJ_THREAD_SUSPENDED, &rt_test_data[i].thread);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create thread", status);
+ return -620;
+ }
+ }
+
+ /* Start threads! */
+ for (i=0; i<THREADS; ++i) {
+ pj_thread_resume(rt_test_data[i].thread);
+ }
+
+ /* Sleep for some time. */
+ pj_thread_sleep(INTERVAL * 1000);
+
+ /* Signal thread to stop. */
+ rt_stop = PJ_TRUE;
+
+ /* Wait threads to complete. */
+ for (i=0; i<THREADS; ++i) {
+ pj_thread_join(rt_test_data[i].thread);
+ pj_thread_destroy(rt_test_data[i].thread);
+ }
+
+ /* Gather statistics. */
+ pj_memset(&total_time, 0, sizeof(total_time));
+ pj_memset(&zero_time, 0, sizeof(zero_time));
+ usec_rt = total_sent = total_recv = 0;
+ for (i=0; i<THREADS; ++i) {
+ total_sent += rt_test_data[i].sent_request_count;
+ total_recv += rt_test_data[i].recv_response_count;
+ pj_add_timestamp(&total_time, &rt_test_data[i].total_rt_time);
+ }
+
+ /* Display statistics. */
+ if (total_recv)
+ total_time.u64 = total_time.u64/total_recv;
+ else
+ total_time.u64 = 0;
+ usec_rt = pj_elapsed_usec(&zero_time, &total_time);
+ PJ_LOG(3,("", " done."));
+ PJ_LOG(3,("", " total %d messages sent", total_sent));
+ if (total_sent-total_recv)
+ PJ_LOG(2,("", " total %d messages LOST", total_sent-total_recv));
+ else
+ PJ_LOG(3,("", " no message was lost"));
+ PJ_LOG(3,("", " average round-trip=%d usec", usec_rt));
+
+ pjsip_endpt_destroy_pool(endpt, pool);
+
+ if (is_reliable && (total_sent != total_recv)) {
+ PJ_LOG(3,("", " error: %d messages lost", total_sent-total_recv));
+ return -650;
+ }
+ return 0;
+}
diff --git a/pjsip/src/test-pjsip/transport_udp_test.c b/pjsip/src/test-pjsip/transport_udp_test.c
new file mode 100644
index 00000000..1af74ed0
--- /dev/null
+++ b/pjsip/src/test-pjsip/transport_udp_test.c
@@ -0,0 +1,98 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "test.h"
+#include <pjsip_core.h>
+#include <pjlib.h>
+
+
+/*
+ * UDP transport test.
+ */
+int transport_udp_test(void)
+{
+ enum { SEND_RECV_LOOP = 2 };
+ pjsip_transport *udp_tp, *tp;
+ pj_sockaddr_in addr, rem_addr;
+ pj_str_t s;
+ pj_status_t status;
+ int i;
+
+ pj_sockaddr_in_init(&addr, NULL, TEST_UDP_PORT);
+
+ /* Start UDP transport. */
+ status = pjsip_udp_transport_start( endpt, &addr, NULL, 1, &udp_tp);
+ if (status != PJ_SUCCESS) {
+ app_perror(" Error: unable to start UDP transport", status);
+ return -10;
+ }
+
+ /* UDP transport must have initial reference counter set to 1. */
+ if (pj_atomic_get(udp_tp->ref_cnt) != 1)
+ return -20;
+
+ /* Test basic transport attributes */
+ status = generic_transport_test(udp_tp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Test that transport manager is returning the correct
+ * transport.
+ */
+ pj_sockaddr_in_init(&rem_addr, pj_cstr(&s, "1.1.1.1"), 80);
+ status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_UDP,
+ &rem_addr, sizeof(rem_addr),
+ &tp);
+ if (status != PJ_SUCCESS)
+ return -50;
+ if (tp != udp_tp)
+ return -60;
+
+ /* pjsip_endpt_acquire_transport() adds reference, so we need
+ * to decrement it.
+ */
+ pjsip_transport_dec_ref(tp);
+
+ /* Check again that reference counter is 1. */
+ if (pj_atomic_get(udp_tp->ref_cnt) != 1)
+ return -70;
+
+ /* Basic transport's send/receive loopback test. */
+ pj_sockaddr_in_init(&rem_addr, pj_cstr(&s, "127.0.0.1"), TEST_UDP_PORT);
+ for (i=0; i<SEND_RECV_LOOP; ++i) {
+ status = transport_send_recv_test(PJSIP_TRANSPORT_UDP, tp, &rem_addr);
+ if (status != 0)
+ return status;
+ }
+
+ /* Multi-threaded round-trip test. */
+ status = transport_rt_test(PJSIP_TRANSPORT_UDP, tp, &rem_addr);
+ if (status != 0)
+ return status;
+
+ /* Check again that reference counter is 1. */
+ if (pj_atomic_get(udp_tp->ref_cnt) != 1)
+ return -80;
+
+ /* Destroy this transport. */
+ pjsip_transport_dec_ref(udp_tp);
+
+ /* Done */
+ return 0;
+}
diff --git a/pjsip/src/test-pjsip/txdata_test.c b/pjsip/src/test-pjsip/txdata_test.c
new file mode 100644
index 00000000..08cad024
--- /dev/null
+++ b/pjsip/src/test-pjsip/txdata_test.c
@@ -0,0 +1,320 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "test.h"
+#include <pjsip_core.h>
+#include <pjlib.h>
+
+#define HFIND(msg,h,H) ((pjsip_##h##_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_##H, NULL))
+
+/*
+ * This tests various core message creation functions.
+ */
+int txdata_test(void)
+{
+ pj_status_t status;
+ pj_str_t target, from, to, contact, body;
+ pjsip_rx_data dummy_rdata;
+ pjsip_tx_data *invite, *invite2, *cancel, *response, *ack;
+
+ /* Create INVITE request. */
+ target = pj_str("tel:+1");
+ from = pj_str("tel:+0");
+ to = pj_str("tel:+1");
+ contact = pj_str("Bob <sip:+0@example.com;user=phone>");
+ body = pj_str("Hello world!");
+
+ status = pjsip_endpt_create_request( endpt, &pjsip_invite_method, &target,
+ &from, &to, &contact, NULL, 10, &body,
+ &invite);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create request", status);
+ return -10;
+ }
+
+ /* Buffer must be invalid. */
+ if (pjsip_tx_data_is_valid(invite) != 0) {
+ PJ_LOG(3,("", " error: buffer must be invalid"));
+ return -14;
+ }
+ /* Reference counter must be set to 1. */
+ if (pj_atomic_get(invite->ref_cnt) != 1) {
+ PJ_LOG(3,("", " error: invalid reference counter"));
+ return -15;
+ }
+ /* Check message type. */
+ if (invite->msg->type != PJSIP_REQUEST_MSG)
+ return -16;
+ /* Check method. */
+ if (invite->msg->line.req.method.id != PJSIP_INVITE_METHOD)
+ return -17;
+
+ /* Check that mandatory headers are present. */
+ if (HFIND(invite->msg, from, FROM) == 0)
+ return -20;
+ if (HFIND(invite->msg, to, TO) == 0)
+ return -21;
+ if (HFIND(invite->msg, contact, CONTACT) == 0)
+ return -22;
+ if (HFIND(invite->msg, cid, CALL_ID) == 0)
+ return -23;
+ if (HFIND(invite->msg, cseq, CSEQ) == 0)
+ return -24;
+ do {
+ pjsip_via_hdr *via = HFIND(invite->msg, via, VIA);
+ if (via == NULL)
+ return -25;
+ /* Branch param must be empty. */
+ if (via->branch_param.slen != 0)
+ return -26;
+ } while (0);
+ if (invite->msg->body == NULL)
+ return -28;
+
+ /* Create another INVITE request from first request. */
+ status = pjsip_endpt_create_request_from_hdr( endpt, &pjsip_invite_method,
+ invite->msg->line.req.uri,
+ HFIND(invite->msg,from,FROM),
+ HFIND(invite->msg,to,TO),
+ HFIND(invite->msg,contact,CONTACT),
+ HFIND(invite->msg,cid,CALL_ID),
+ 10, &body, &invite2);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: create second request failed", status);
+ return -30;
+ }
+
+ /* Buffer must be invalid. */
+ if (pjsip_tx_data_is_valid(invite2) != 0) {
+ PJ_LOG(3,("", " error: buffer must be invalid"));
+ return -34;
+ }
+ /* Reference counter must be set to 1. */
+ if (pj_atomic_get(invite2->ref_cnt) != 1) {
+ PJ_LOG(3,("", " error: invalid reference counter"));
+ return -35;
+ }
+ /* Check message type. */
+ if (invite2->msg->type != PJSIP_REQUEST_MSG)
+ return -36;
+ /* Check method. */
+ if (invite2->msg->line.req.method.id != PJSIP_INVITE_METHOD)
+ return -37;
+
+ /* Check that mandatory headers are again present. */
+ if (HFIND(invite2->msg, from, FROM) == 0)
+ return -40;
+ if (HFIND(invite2->msg, to, TO) == 0)
+ return -41;
+ if (HFIND(invite2->msg, contact, CONTACT) == 0)
+ return -42;
+ if (HFIND(invite2->msg, cid, CALL_ID) == 0)
+ return -43;
+ if (HFIND(invite2->msg, cseq, CSEQ) == 0)
+ return -44;
+ if (HFIND(invite2->msg, via, VIA) == 0)
+ return -45;
+ /*
+ if (HFIND(invite2->msg, ctype, CONTENT_TYPE) == 0)
+ return -46;
+ if (HFIND(invite2->msg, clen, CONTENT_LENGTH) == 0)
+ return -47;
+ */
+ if (invite2->msg->body == NULL)
+ return -48;
+
+ /* Done checking invite2. We can delete this. */
+ if (pjsip_tx_data_dec_ref(invite2) != PJSIP_EBUFDESTROYED) {
+ PJ_LOG(3,("", " error: request buffer not destroyed!"));
+ return -49;
+ }
+
+ /* Initialize dummy rdata (to simulate receiving a request)
+ * We should never do this in real application, as there are many
+ * many more fields need to be initialized!!
+ */
+ dummy_rdata.msg_info.call_id = (HFIND(invite->msg, cid, CALL_ID))->id;
+ dummy_rdata.msg_info.clen = NULL;
+ dummy_rdata.msg_info.cseq = HFIND(invite->msg, cseq, CSEQ);
+ dummy_rdata.msg_info.ctype = NULL;
+ dummy_rdata.msg_info.from = HFIND(invite->msg, from, FROM);
+ dummy_rdata.msg_info.max_fwd = NULL;
+ dummy_rdata.msg_info.msg = invite->msg;
+ dummy_rdata.msg_info.record_route = NULL;
+ dummy_rdata.msg_info.require = NULL;
+ dummy_rdata.msg_info.route = NULL;
+ dummy_rdata.msg_info.to = HFIND(invite->msg, to, TO);
+ dummy_rdata.msg_info.via = HFIND(invite->msg, via, VIA);
+
+ /* Create a response message for the request. */
+ status = pjsip_endpt_create_response( endpt, &dummy_rdata, 301, NULL,
+ &response);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create response", status);
+ return -50;
+ }
+
+ /* Buffer must be invalid. */
+ if (pjsip_tx_data_is_valid(response) != 0) {
+ PJ_LOG(3,("", " error: buffer must be invalid"));
+ return -54;
+ }
+ /* Check reference counter. */
+ if (pj_atomic_get(response->ref_cnt) != 1) {
+ PJ_LOG(3,("", " error: invalid ref count in response"));
+ return -55;
+ }
+ /* Check message type. */
+ if (response->msg->type != PJSIP_RESPONSE_MSG)
+ return -56;
+ /* Check correct status is set. */
+ if (response->msg->line.status.code != 301)
+ return -57;
+
+ /* Check that mandatory headers are again present. */
+ if (HFIND(response->msg, from, FROM) == 0)
+ return -60;
+ if (HFIND(response->msg, to, TO) == 0)
+ return -61;
+ /*
+ if (HFIND(response->msg, contact, CONTACT) == 0)
+ return -62;
+ */
+ if (HFIND(response->msg, cid, CALL_ID) == 0)
+ return -63;
+ if (HFIND(response->msg, cseq, CSEQ) == 0)
+ return -64;
+ if (HFIND(response->msg, via, VIA) == 0)
+ return -65;
+
+ /* This response message will be used later when creating ACK */
+
+ /* Create CANCEL request for the original request. */
+ status = pjsip_endpt_create_cancel( endpt, invite, &cancel);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create CANCEL request", status);
+ return -80;
+ }
+
+ /* Buffer must be invalid. */
+ if (pjsip_tx_data_is_valid(cancel) != 0) {
+ PJ_LOG(3,("", " error: buffer must be invalid"));
+ return -84;
+ }
+ /* Check reference counter. */
+ if (pj_atomic_get(cancel->ref_cnt) != 1) {
+ PJ_LOG(3,("", " error: invalid ref count in CANCEL request"));
+ return -85;
+ }
+ /* Check message type. */
+ if (cancel->msg->type != PJSIP_REQUEST_MSG)
+ return -86;
+ /* Check method. */
+ if (cancel->msg->line.req.method.id != PJSIP_CANCEL_METHOD)
+ return -87;
+
+ /* Check that mandatory headers are again present. */
+ if (HFIND(cancel->msg, from, FROM) == 0)
+ return -90;
+ if (HFIND(cancel->msg, to, TO) == 0)
+ return -91;
+ /*
+ if (HFIND(cancel->msg, contact, CONTACT) == 0)
+ return -92;
+ */
+ if (HFIND(cancel->msg, cid, CALL_ID) == 0)
+ return -93;
+ if (HFIND(cancel->msg, cseq, CSEQ) == 0)
+ return -94;
+ if (HFIND(cancel->msg, via, VIA) == 0)
+ return -95;
+
+ /* Done checking CANCEL request. */
+ if (pjsip_tx_data_dec_ref(cancel) != PJSIP_EBUFDESTROYED) {
+ PJ_LOG(3,("", " error: response buffer not destroyed!"));
+ return -99;
+ }
+
+ /* Modify dummy_rdata to simulate receiving response. */
+ pj_memset(&dummy_rdata, 0, sizeof(dummy_rdata));
+ dummy_rdata.msg_info.msg = response->msg;
+ dummy_rdata.msg_info.to = HFIND(response->msg, to, TO);
+
+ /* Create ACK request */
+ status = pjsip_endpt_create_ack( endpt, invite, &dummy_rdata, &ack );
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(3,("", " error: unable to create ACK"));
+ return -100;
+ }
+ /* Buffer must be invalid. */
+ if (pjsip_tx_data_is_valid(ack) != 0) {
+ PJ_LOG(3,("", " error: buffer must be invalid"));
+ return -104;
+ }
+ /* Check reference counter. */
+ if (pj_atomic_get(ack->ref_cnt) != 1) {
+ PJ_LOG(3,("", " error: invalid ref count in ACK request"));
+ return -105;
+ }
+ /* Check message type. */
+ if (ack->msg->type != PJSIP_REQUEST_MSG)
+ return -106;
+ /* Check method. */
+ if (ack->msg->line.req.method.id != PJSIP_ACK_METHOD)
+ return -107;
+ /* Check Request-URI is present. */
+ if (ack->msg->line.req.uri == NULL)
+ return -108;
+
+ /* Check that mandatory headers are again present. */
+ if (HFIND(ack->msg, from, FROM) == 0)
+ return -110;
+ if (HFIND(ack->msg, to, TO) == 0)
+ return -111;
+ if (HFIND(ack->msg, cid, CALL_ID) == 0)
+ return -112;
+ if (HFIND(ack->msg, cseq, CSEQ) == 0)
+ return -113;
+ if (HFIND(ack->msg, via, VIA) == 0)
+ return -114;
+ if (ack->msg->body != NULL)
+ return -115;
+
+ /* Done checking invite message. */
+ if (pjsip_tx_data_dec_ref(invite) != PJSIP_EBUFDESTROYED) {
+ PJ_LOG(3,("", " error: response buffer not destroyed!"));
+ return -120;
+ }
+
+ /* Done checking response message. */
+ if (pjsip_tx_data_dec_ref(response) != PJSIP_EBUFDESTROYED) {
+ PJ_LOG(3,("", " error: response buffer not destroyed!"));
+ return -130;
+ }
+
+ /* Done checking ack message. */
+ if (pjsip_tx_data_dec_ref(ack) != PJSIP_EBUFDESTROYED) {
+ PJ_LOG(3,("", " error: response buffer not destroyed!"));
+ return -140;
+ }
+
+ /* Done. */
+ return 0;
+}
+
diff --git a/pjsip/src/test-pjsip/uri_test.c b/pjsip/src/test-pjsip/uri_test.c
index 29a16dd5..647397a2 100644
--- a/pjsip/src/test-pjsip/uri_test.c
+++ b/pjsip/src/test-pjsip/uri_test.c
@@ -783,7 +783,7 @@ on_return:
return status;
}
-pj_status_t uri_test()
+int uri_test()
{
unsigned i, loop;
pj_pool_t *pool;