From 483805f79570115ab95c69698792d238c1719b1b Mon Sep 17 00:00:00 2001 From: Jason Parker Date: Mon, 11 Mar 2013 15:09:56 -0500 Subject: Import pjproject-2.1 --- pjsip/build/Makefile | 2 +- pjsip/include/pjsip/sip_auth.h | 79 +++- pjsip/include/pjsip/sip_config.h | 50 +- pjsip/include/pjsip/sip_endpoint.h | 73 ++- pjsip/include/pjsip/sip_transport.h | 110 ++++- pjsip/include/pjsip/sip_transport_tls.h | 42 +- pjsip/include/pjsip/sip_types.h | 7 +- pjsip/include/pjsip/sip_uri.h | 10 +- pjsip/include/pjsip/sip_util.h | 9 +- pjsip/include/pjsua-lib/pjsua.h | 346 +++++++++++++- pjsip/include/pjsua-lib/pjsua_internal.h | 31 +- pjsip/src/pjsip-simple/publishc.c | 9 +- pjsip/src/pjsip-ua/sip_100rel.c | 6 +- pjsip/src/pjsip-ua/sip_inv.c | 100 +++- pjsip/src/pjsip-ua/sip_reg.c | 11 +- pjsip/src/pjsip-ua/sip_replaces.c | 19 +- pjsip/src/pjsip-ua/sip_timer.c | 29 +- pjsip/src/pjsip/sip_auth_client.c | 17 +- pjsip/src/pjsip/sip_auth_server.c | 47 +- pjsip/src/pjsip/sip_config.c | 4 +- pjsip/src/pjsip/sip_dialog.c | 23 +- pjsip/src/pjsip/sip_endpoint.c | 160 +++++-- pjsip/src/pjsip/sip_parser.c | 11 +- pjsip/src/pjsip/sip_tel_uri.c | 6 +- pjsip/src/pjsip/sip_transaction.c | 43 +- pjsip/src/pjsip/sip_transport.c | 243 ++++++++-- pjsip/src/pjsip/sip_transport_tcp.c | 174 ++++--- pjsip/src/pjsip/sip_transport_tls.c | 191 +++++--- pjsip/src/pjsip/sip_ua_layer.c | 50 +- pjsip/src/pjsip/sip_uri.c | 7 +- pjsip/src/pjsip/sip_util_proxy.c | 4 +- pjsip/src/pjsua-lib/pjsua_acc.c | 290 ++++++++++-- pjsip/src/pjsua-lib/pjsua_aud.c | 36 +- pjsip/src/pjsua-lib/pjsua_call.c | 724 ++++++++++++++++++----------- pjsip/src/pjsua-lib/pjsua_core.c | 178 +++++-- pjsip/src/pjsua-lib/pjsua_dump.c | 30 +- pjsip/src/pjsua-lib/pjsua_media.c | 767 ++++++++++++++++++++++--------- pjsip/src/pjsua-lib/pjsua_pres.c | 76 ++- pjsip/src/pjsua-lib/pjsua_vid.c | 32 +- pjsip/src/test/tsx_uac_test.c | 34 +- pjsip/src/test/tsx_uas_test.c | 76 +-- pjsip/src/test/uri_test.c | 24 +- 42 files changed, 3174 insertions(+), 1006 deletions(-) (limited to 'pjsip') diff --git a/pjsip/build/Makefile b/pjsip/build/Makefile index 2605be0..0f8fd13 100644 --- a/pjsip/build/Makefile +++ b/pjsip/build/Makefile @@ -138,7 +138,7 @@ pjsip-simple: pjsua-lib: $(MAKE) -f $(RULES_MAK) APP=PJSUA_LIB app=pjsua-lib $(PJSUA_LIB_LIB) -pjsip-test: +pjsip-test: pjsip $(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $(TEST_EXE) .PHONY: ../lib/pjsip.ko diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h index 613e0d5..cd59372 100644 --- a/pjsip/include/pjsip/sip_auth.h +++ b/pjsip/include/pjsip/sip_auth.h @@ -1,4 +1,4 @@ -/* $Id: sip_auth.h 3553 2011-05-05 06:14:19Z nanang $ */ +/* $Id: sip_auth.h 4214 2012-07-25 14:29:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -275,6 +275,37 @@ typedef pj_status_t pjsip_auth_lookup_cred( pj_pool_t *pool, const pj_str_t *acc_name, pjsip_cred_info *cred_info ); + +/** + * This structure describes input param for credential lookup. + */ +typedef struct pjsip_auth_lookup_cred_param +{ + pj_str_t realm; /**< Realm to find the account. */ + pj_str_t acc_name; /**< Account name to look for. */ + pjsip_rx_data *rdata; /**< Incoming request to be authenticated. */ + +} pjsip_auth_lookup_cred_param; + + +/** + * Type of function to lookup credential for the specified name. + * + * @param pool Pool to initialize the credential info. + * @param param The input param for credential lookup. + * @param cred_info The structure to put the credential when it's found. + * + * @return The function MUST return PJ_SUCCESS when it found + * a correct credential for the specified account and + * realm. Otherwise it may return PJSIP_EAUTHACCNOTFOUND + * or PJSIP_EAUTHACCDISABLED. + */ +typedef pj_status_t pjsip_auth_lookup_cred2( + pj_pool_t *pool, + const pjsip_auth_lookup_cred_param *param, + pjsip_cred_info *cred_info ); + + /** Flag to specify that server is a proxy. */ #define PJSIP_AUTH_SRV_IS_PROXY 1 @@ -286,7 +317,8 @@ typedef struct pjsip_auth_srv pj_str_t realm; /**< Realm to serve. */ pj_bool_t is_proxy; /**< Will issue 407 instead of 401 */ pjsip_auth_lookup_cred *lookup; /**< Lookup function. */ - + pjsip_auth_lookup_cred2 *lookup2; /**< Lookup function with additional + info in its input param. */ } pjsip_auth_srv; @@ -433,6 +465,49 @@ PJ_DECL(pj_status_t) pjsip_auth_srv_init( pj_pool_t *pool, unsigned options ); +/** + * This structure describes initialization settings of server authorization + * session. + */ +typedef struct pjsip_auth_srv_init_param +{ + /** + * Realm to be served by the server. + */ + const pj_str_t *realm; + + /** + * Account lookup function. + */ + pjsip_auth_lookup_cred2 *lookup2; + + /** + * Options, bitmask of: + * - PJSIP_AUTH_SRV_IS_PROXY: to specify that the server will authorize + * clients as a proxy server (instead of as UAS), which means that + * Proxy-Authenticate will be used instead of WWW-Authenticate. + */ + unsigned options; + +} pjsip_auth_srv_init_param; + + +/** + * Initialize server authorization session data structure to serve the + * specified realm and to use lookup_func function to look for the credential + * info. + * + * @param pool Pool used to initialize the authentication server. + * @param auth_srv The authentication server structure. + * @param param The initialization param. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjsip_auth_srv_init2( + pj_pool_t *pool, + pjsip_auth_srv *auth_srv, + const pjsip_auth_srv_init_param *param); + /** * Request the authorization server framework to verify the authorization * information in the specified request in rdata. diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h index 9035b1e..bde0777 100644 --- a/pjsip/include/pjsip/sip_config.h +++ b/pjsip/include/pjsip/sip_config.h @@ -1,4 +1,4 @@ -/* $Id: sip_config.h 4172 2012-06-19 14:35:18Z nanang $ */ +/* $Id: sip_config.h 4285 2012-10-19 04:23:57Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -73,18 +73,41 @@ typedef struct pjsip_cfg_t /** * Specify port number should be allowed to appear in To and From * header. Note that RFC 3261 disallow this, see Table 1 in section - * 19.1.1 of the RFC. Default is PJSIP_ALLOW_PORT_IN_FROMTO_HDR. + * 19.1.1 of the RFC. + * + * Default is PJSIP_ALLOW_PORT_IN_FROMTO_HDR. */ pj_bool_t allow_port_in_fromto_hdr; + /** + * Accept call replace in early state when invite is not initiated + * by the user agent. RFC 3891 Section 3 disallows this, however, + * for better interoperability reason, this might be ignored. + * + * Default is PJSIP_ACCEPT_REPLACE_IN_EARLY_STATE. + */ + pj_bool_t accept_replace_in_early_state; + + /** + * Allow hash character ('#') to appear in outgoing URIs. See + * https://trac.pjsip.org/repos/ticket/1569. + * + * Default is PJ_FALSE. + */ + pj_bool_t allow_tx_hash_in_uri; + /** * Disable rport in request. + * + * Default is PJ_FALSE. */ pj_bool_t disable_rport; /** * Disable automatic switching from UDP to TCP if outgoing request - * is greater than 1300 bytes. See PJSIP_DONT_SWITCH_TO_TCP. + * is greater than 1300 bytes. + * + * Default is PJSIP_DONT_SWITCH_TO_TCP. */ pj_bool_t disable_tcp_switch; @@ -268,6 +291,21 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void) #endif +/** + * Accept call replace in early state when invite is not initiated + * by the user agent. RFC 3891 Section 3 disallows this, however, + * for better interoperability reason, this might be ignored. + * + * This option can also be controlled at run-time by the + * \a accept_replace_in_early_state setting in pjsip_cfg_t. + * + * Default is 0 (no). + */ +#ifndef PJSIP_ACCEPT_REPLACE_IN_EARLY_STATE +# define PJSIP_ACCEPT_REPLACE_IN_EARLY_STATE 0 +#endif + + /** * This setting controls the threshold of the UDP packet, which if it's * larger than this value the request will be sent with TCP. This setting @@ -362,9 +400,13 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void) * response is received, the response will be discarded since its Via * sent-by now contains address that is different than the transport * address. + * + * Update: + * As of version 2.1, the default value is 0. This change was part of + * https://trac.pjsip.org/repos/ticket/1412 */ #ifndef PJSIP_CHECK_VIA_SENT_BY -# define PJSIP_CHECK_VIA_SENT_BY 1 +# define PJSIP_CHECK_VIA_SENT_BY 0 #endif diff --git a/pjsip/include/pjsip/sip_endpoint.h b/pjsip/include/pjsip/sip_endpoint.h index ac3f525..63c65d7 100644 --- a/pjsip/include/pjsip/sip_endpoint.h +++ b/pjsip/include/pjsip/sip_endpoint.h @@ -1,4 +1,4 @@ -/* $Id: sip_endpoint.h 4154 2012-06-05 10:41:17Z bennylp $ */ +/* $Id: sip_endpoint.h 4275 2012-10-04 06:11:58Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -213,6 +213,77 @@ PJ_DECL(pj_status_t) pjsip_endpt_register_module( pjsip_endpoint *endpt, PJ_DECL(pj_status_t) pjsip_endpt_unregister_module( pjsip_endpoint *endpt, pjsip_module *module ); +/** + * This describes additional parameters to pjsip_endpt_process_rx_data() + * function. Application MUST call pjsip_process_rdata_param_default() to + * initialize this structure. + */ +typedef struct pjsip_process_rdata_param +{ + /** + * Specify the minimum priority number of the modules that are allowed + * to process the message. Default is zero to allow all modules to + * process the message. + */ + unsigned start_prio; + + /** + * Specify the pointer of the module where processing will start. + * The default is NULL, meaning processing will start from the start + * of the module list. + */ + void *start_mod; + + /** + * Set to N, then processing will start at Nth module after start + * module (where start module can be an explicit module as specified + * by \a start_mod or the start of module list when \a start_mod is + * NULL). For example, if set to 1, then processing will start from + * the next module after start module. Default is zero. + */ + unsigned idx_after_start; + + /** + * Print nothing to log. Default is PJ_FALSE. + */ + pj_bool_t silent; + +} pjsip_process_rdata_param; + +/** + * Initialize with default. + * + * @param p The param. + */ +PJ_DECL(void) pjsip_process_rdata_param_default(pjsip_process_rdata_param *p); + +/** + * Manually distribute the specified pjsip_rx_data to registered modules. + * Normally application does not need to call this function because received + * messages will be given to endpoint automatically by transports. + * + * Application can use this function when it has postponed the processing of + * an incoming message, for example to perform long operations such as + * database operation or to consult other servers to decide what to do with + * the message. In this case, application clones the original rdata, return + * from the callback, and perform the long operation. Upon completing the + * long operation, it resumes pjsip's module processing by calling this + * function, and then free the cloned rdata. + * + * @param endpt The endpoint instance. + * @param rdata The rdata to be distributed. + * @param p Optional pointer to param to specify from which module + * the processing should start. + * @param p_handled Optional pointer to receive last return value of + * module's \a on_rx_request() or \a on_rx_response() + * callback. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjsip_endpt_process_rx_data(pjsip_endpoint *endpt, + pjsip_rx_data *rdata, + pjsip_process_rdata_param *p, + pj_bool_t *p_handled); /** * Create pool from the endpoint. All SIP components should allocate their diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h index c8ea6fd..21b2238 100644 --- a/pjsip/include/pjsip/sip_transport.h +++ b/pjsip/include/pjsip/sip_transport.h @@ -1,4 +1,4 @@ -/* $Id: sip_transport.h 4173 2012-06-20 10:39:05Z ming $ */ +/* $Id: sip_transport.h 4275 2012-10-04 06:11:58Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -427,6 +427,42 @@ struct pjsip_rx_data */ PJ_DECL(char*) pjsip_rx_data_get_info(pjsip_rx_data *rdata); +/** + * Clone pjsip_rx_data. This will duplicate the contents of + * pjsip_rx_data and add reference count to the transport. + * Once application has finished using the cloned pjsip_rx_data, + * it must release it by calling #pjsip_rx_data_free_cloned(). + * + * By default (if flags is set to zero), this function copies the + * transport pointer in \a tp_info, duplicates the \a pkt_info, + * perform deep clone of the \a msg_info parts of the rdata, and + * fills the \a endpt_info (i.e. the \a mod_data) with zeros. + * + * @param src The source to be cloned. + * @param flags Optional flags. Must be zero for now. + * @param p_rdata Pointer to receive the cloned rdata. + * + * @return PJ_SUCCESS on success or the appropriate error. + */ +PJ_DECL(pj_status_t) pjsip_rx_data_clone(const pjsip_rx_data *src, + unsigned flags, + pjsip_rx_data **p_rdata); + +/** + * Free cloned pjsip_rx_data. This function must be and must only + * be called for a cloned pjsip_rx_data. Specifically, it must NOT + * be called for the original pjsip_rx_data that is returned by + * transports. + * + * This function will free the memory used by the pjsip_rx_data and + * decrement the transport reference counter. + * + * @param rdata The receive data buffer. + * + * @return PJ_SUCCESS on success or the appropriate error. + */ +PJ_DECL(pj_status_t) pjsip_rx_data_free_cloned(pjsip_rx_data *rdata); + /***************************************************************************** * @@ -1076,6 +1112,8 @@ PJ_DECL(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool, * In this implementation, it will only select the transport based on * the transport type in the request. * + * @see pjsip_tpmgr_find_local_addr2() + * * @param tpmgr The transport manager. * @param pool Pool to allocate memory for the IP address. * @param type Destination address to contact. @@ -1092,6 +1130,76 @@ PJ_DECL(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr, pj_str_t *ip_addr, int *port); +/** + * Parameter for pjsip_tpmgr_find_local_addr2() function. + */ +typedef struct pjsip_tpmgr_fla2_param +{ + /** + * Specify transport type to use. This must be set. + */ + pjsip_transport_type_e tp_type; + + /** + * Optional pointer to preferred transport, if any. + */ + const pjsip_tpselector *tp_sel; + + /** + * Destination host, if known. The destination host is needed + * if \a local_if field below is set. + */ + pj_str_t dst_host; + + /** + * Specify if the function should return which local interface + * to use for the specified destination in \a dst_host. By definition, + * the returned address will always be local interface address. + */ + pj_bool_t local_if; + + /** + * The returned address. + */ + pj_str_t ret_addr; + + /** + * The returned port. + */ + pj_uint16_t ret_port; + + /** + * Returned pointer to the transport. Only set if local_if is set. + */ + const void *ret_tp; + +} pjsip_tpmgr_fla2_param; + +/** + * Initialize with default values. + * + * @param prm The parameter to be initialized. + */ +PJ_DECL(void) pjsip_tpmgr_fla2_param_default(pjsip_tpmgr_fla2_param *prm); + +/** + * Find out the appropriate local address info (IP address and port) to + * advertise in Contact or Via header header based on the remote address + * to be contacted. The local address info would be the address name of the + * transport or listener which will be used to send the request. + * + * @see pjsip_tpmgr_find_local_addr() + * + * @param tpmgr The transport manager. + * @param pool Pool to allocate memory for the IP address. + * @param param Function input and output parameters. + * + * @return PJ_SUCCESS, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsip_tpmgr_find_local_addr2(pjsip_tpmgr *tpmgr, + pj_pool_t *pool, + pjsip_tpmgr_fla2_param *prm); + /** * Return number of transports currently registered to the transport * manager. diff --git a/pjsip/include/pjsip/sip_transport_tls.h b/pjsip/include/pjsip/sip_transport_tls.h index cbc1d75..8164c63 100644 --- a/pjsip/include/pjsip/sip_transport_tls.h +++ b/pjsip/include/pjsip/sip_transport_tls.h @@ -1,4 +1,4 @@ -/* $Id: sip_transport_tls.h 3999 2012-03-30 07:10:13Z bennylp $ */ +/* $Id: sip_transport_tls.h 4262 2012-09-20 06:00:23Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -262,6 +262,8 @@ PJ_INLINE(void) pjsip_tls_setting_copy(pj_pool_t *pool, * instance of SIP TLS transport factory and register it to the * transport manager. * + * See also #pjsip_tls_transport_start2() which supports IPv6. + * * @param endpt The SIP endpoint. * @param opt Optional TLS settings. * @param local Optional local address to bind, or specify the @@ -294,7 +296,43 @@ PJ_DECL(pj_status_t) pjsip_tls_transport_start(pjsip_endpoint *endpt, unsigned async_cnt, pjsip_tpfactory **p_factory); - +/** + * Variant of #pjsip_tls_transport_start() that supports IPv6. To instantiate + * IPv6 listener, set the address family of the "local" argument to IPv6 + * (the host and port part may be left unspecified if not desired, i.e. by + * filling them with zeroes). + * + * @param endpt The SIP endpoint. + * @param opt Optional TLS settings. + * @param local Optional local address to bind, or specify the + * address to bind the server socket to. Both IP + * interface address and port fields are optional. + * If IP interface address is not specified, socket + * will be bound to any address. If port is not + * specified, socket will be bound to any port + * selected by the operating system. + * @param a_name Optional published address, which is the address to be + * advertised as the address of this SIP transport. + * If this argument is NULL, then the bound address + * will be used as the published address. + * @param async_cnt Number of simultaneous asynchronous accept() + * operations to be supported. It is recommended that + * the number here corresponds to the number of + * processors in the system (or the number of SIP + * worker threads). + * @param p_factory Optional pointer to receive the instance of the + * SIP TLS transport factory just created. + * + * @return PJ_SUCCESS when the transport has been successfully + * started and registered to transport manager, or + * the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsip_tls_transport_start2(pjsip_endpoint *endpt, + const pjsip_tls_setting *opt, + const pj_sockaddr *local, + const pjsip_host_port *a_name, + unsigned async_cnt, + pjsip_tpfactory **p_factory); PJ_END_DECL diff --git a/pjsip/include/pjsip/sip_types.h b/pjsip/include/pjsip/sip_types.h index 8cd98ae..9809983 100644 --- a/pjsip/include/pjsip/sip_types.h +++ b/pjsip/include/pjsip/sip_types.h @@ -1,4 +1,4 @@ -/* $Id: sip_types.h 3553 2011-05-05 06:14:19Z nanang $ */ +/* $Id: sip_types.h 4262 2012-09-20 06:00:23Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -92,7 +92,10 @@ typedef enum pjsip_transport_type_e PJSIP_TRANSPORT_UDP6 = PJSIP_TRANSPORT_UDP + PJSIP_TRANSPORT_IPV6, /** TCP over IPv6 */ - PJSIP_TRANSPORT_TCP6 = PJSIP_TRANSPORT_TCP + PJSIP_TRANSPORT_IPV6 + PJSIP_TRANSPORT_TCP6 = PJSIP_TRANSPORT_TCP + PJSIP_TRANSPORT_IPV6, + + /** TLS over IPv6 */ + PJSIP_TRANSPORT_TLS6 = PJSIP_TRANSPORT_TLS + PJSIP_TRANSPORT_IPV6 } pjsip_transport_type_e; diff --git a/pjsip/include/pjsip/sip_uri.h b/pjsip/include/pjsip/sip_uri.h index 849392f..47b63bd 100644 --- a/pjsip/include/pjsip/sip_uri.h +++ b/pjsip/include/pjsip/sip_uri.h @@ -1,4 +1,4 @@ -/* $Id: sip_uri.h 3553 2011-05-05 06:14:19Z nanang $ */ +/* $Id: sip_uri.h 4370 2013-02-26 05:30:00Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -226,12 +226,12 @@ struct pjsip_uri }; /** - * This macro checks that the URL is a "sip:" or "sips:" URL. + * This macro checks that the URL is a "sip:" URL. * @param url The URL (pointer to) * @return non-zero if TRUE. */ #define PJSIP_URI_SCHEME_IS_SIP(url) \ - (pj_strnicmp2(pjsip_uri_get_scheme(url), "sip", 3)==0) + (pj_stricmp2(pjsip_uri_get_scheme(url), "sip")==0) /** * This macro checks that the URL is a "sips:" URL (not SIP). @@ -239,7 +239,7 @@ struct pjsip_uri * @return non-zero if TRUE. */ #define PJSIP_URI_SCHEME_IS_SIPS(url) \ - (pj_strnicmp2(pjsip_uri_get_scheme(url), "sips", 4)==0) + (pj_stricmp2(pjsip_uri_get_scheme(url), "sips")==0) /** * This macro checks that the URL is a "tel:" URL. @@ -247,7 +247,7 @@ struct pjsip_uri * @return non-zero if TRUE. */ #define PJSIP_URI_SCHEME_IS_TEL(url) \ - (pj_strnicmp2(pjsip_uri_get_scheme(url), "tel", 3)==0) + (pj_stricmp2(pjsip_uri_get_scheme(url), "tel")==0) /** diff --git a/pjsip/include/pjsip/sip_util.h b/pjsip/include/pjsip/sip_util.h index 6efa45f..018ad87 100644 --- a/pjsip/include/pjsip/sip_util.h +++ b/pjsip/include/pjsip/sip_util.h @@ -1,4 +1,4 @@ -/* $Id: sip_util.h 3553 2011-05-05 06:14:19Z nanang $ */ +/* $Id: sip_util.h 4347 2013-02-13 10:19:25Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -91,6 +91,13 @@ typedef enum pjsip_redirect_op */ PJSIP_REDIRECT_ACCEPT, + /** + * Accept the redirection to the current target and replace the To + * header in the INVITE request with the current target. The INVITE + * request will be resent to the current target. + */ + PJSIP_REDIRECT_ACCEPT_REPLACE, + /** * Defer the redirection decision, for example to request permission * from the end user. diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 856d853..88c0d46 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -1,4 +1,4 @@ -/* $Id: pjsua.h 4180 2012-06-26 09:37:41Z ming $ */ +/* $Id: pjsua.h 4347 2013-02-13 10:19:25Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -906,9 +906,10 @@ typedef struct pjsua_callback * callback. * - it may delay the processing of the request, for example to request * user permission whether to accept or reject the request. In this - * case, the application MUST set the \a code argument to 202, and - * later calls #pjsua_pres_notify() to accept or reject the - * subscription request. + * case, the application MUST set the \a code argument to 202, then + * IMMEDIATELY calls #pjsua_pres_notify() with state + * PJSIP_EVSUB_STATE_PENDING and later calls #pjsua_pres_notify() + * again to accept or reject the subscription request. * * Any \a code other than 200 and 202 will be treated as 200. * @@ -1124,9 +1125,8 @@ typedef struct pjsua_callback * INVITE request to the specified target, following the previously * received redirection response. * - * Application may accept the redirection to the specified target - * (the default behavior if this callback is implemented), reject - * this target only and make the session continue to try the next + * Application may accept the redirection to the specified target, + * reject this target only and make the session continue to try the next * target in the list if such target exists, stop the whole * redirection process altogether and cause the session to be * disconnected, or defer the decision to ask for user confirmation. @@ -1146,9 +1146,12 @@ typedef struct pjsua_callback * @return Action to be performed for the target. Set this * parameter to one of the value below: * - PJSIP_REDIRECT_ACCEPT: immediately accept the - * redirection (default value). When set, the - * call will immediately resend INVITE request - * to the target. + * redirection. When set, the call will immediately + * resend INVITE request to the target. + * - PJSIP_REDIRECT_ACCEPT_REPLACE: immediately accept + * the redirection and replace the To header with the + * current target. When set, the call will immediately + * resend INVITE request to the target. * - PJSIP_REDIRECT_REJECT: immediately reject this * target. The call will continue retrying with * next target if present, or disconnect the call @@ -1460,6 +1463,15 @@ typedef struct pjsua_config */ pj_bool_t stun_ignore_failure; + /** + * This specifies whether STUN requests for resolving socket mapped + * address should use the new format, i.e: having STUN magic cookie + * in its transaction ID. + * + * Default: PJ_FALSE + */ + pj_bool_t stun_map_use_stun2; + /** * Support for adding and parsing NAT type in the SDP to assist * troubleshooting. The valid values are: @@ -2142,6 +2154,15 @@ typedef struct pjsua_transport_config */ unsigned port; + /** + * Specify the port range for socket binding, relative to the start + * port number specified in \a port. Note that this setting is only + * applicable when the start port number is non zero. + * + * Default value is zero. + */ + unsigned port_range; + /** * Optional address to advertise as the address of this transport. * Application can specify any address or hostname for this field, @@ -2565,6 +2586,147 @@ typedef enum pjsua_call_hold_type # define PJSUA_CALL_HOLD_TYPE_DEFAULT PJSUA_CALL_HOLD_TYPE_RFC3264 #endif +/** + * This enumeration controls the use of STUN in the account. + */ +typedef enum pjsua_stun_use +{ + /** + * Follow the default setting in the global \a pjsua_config. + */ + PJSUA_STUN_USE_DEFAULT, + + /** + * Disable STUN. If STUN is not enabled in the global \a pjsua_config, + * this setting has no effect. + */ + PJSUA_STUN_USE_DISABLED + +} pjsua_stun_use; + +/** + * This enumeration controls the use of ICE settings in the account. + */ +typedef enum pjsua_ice_config_use +{ + /** + * Use the default settings in the global \a pjsua_media_config. + */ + PJSUA_ICE_CONFIG_USE_DEFAULT, + + /** + * Use the custom \a pjsua_ice_config setting in the account. + */ + PJSUA_ICE_CONFIG_USE_CUSTOM + +} pjsua_ice_config_use; + +/** + * This enumeration controls the use of TURN settings in the account. + */ +typedef enum pjsua_turn_config_use +{ + /** + * Use the default setting in the global \a pjsua_media_config. + */ + PJSUA_TURN_CONFIG_USE_DEFAULT, + + /** + * Use the custom \a pjsua_turn_config setting in the account. + */ + PJSUA_TURN_CONFIG_USE_CUSTOM + +} pjsua_turn_config_use; + +/** + * ICE setting. This setting is used in the pjsua_acc_config. + */ +typedef struct pjsua_ice_config +{ + /** + * Enable ICE. + */ + pj_bool_t enable_ice; + + /** + * Set the maximum number of host candidates. + * + * Default: -1 (maximum not set) + */ + int ice_max_host_cands; + + /** + * ICE session options. + */ + pj_ice_sess_options ice_opt; + + /** + * Disable RTCP component. + * + * Default: no + */ + pj_bool_t ice_no_rtcp; + + /** + * Send re-INVITE/UPDATE every after ICE connectivity check regardless + * the default ICE transport address is changed or not. When this is set + * to PJ_FALSE, re-INVITE/UPDATE will be sent only when the default ICE + * transport address is changed. + * + * Default: yes + */ + pj_bool_t ice_always_update; + +} pjsua_ice_config; + +/** + * TURN setting. This setting is used in the pjsua_acc_config. + */ +typedef struct pjsua_turn_config +{ + /** + * Enable TURN candidate in ICE. + */ + pj_bool_t enable_turn; + + /** + * Specify TURN domain name or host name, in in "DOMAIN:PORT" or + * "HOST:PORT" format. + */ + pj_str_t turn_server; + + /** + * Specify the connection type to be used to the TURN server. Valid + * values are PJ_TURN_TP_UDP or PJ_TURN_TP_TCP. + * + * Default: PJ_TURN_TP_UDP + */ + pj_turn_tp_type turn_conn_type; + + /** + * Specify the credential to authenticate with the TURN server. + */ + pj_stun_auth_cred turn_auth_cred; + +} pjsua_turn_config; + +/** + * Specify how IPv6 transport should be used in account config. + */ +typedef enum pjsua_ipv6_use +{ + /** + * IPv6 is not used. + */ + PJSUA_IPV6_DISABLED, + + /** + * IPv6 is enabled. + */ + PJSUA_IPV6_ENABLED + +} pjsua_ipv6_use; + /** * This structure describes account configuration to be specified when * adding a new account with #pjsua_acc_add(). Application MUST initialize @@ -2588,7 +2750,8 @@ typedef struct pjsua_acc_config /** * The full SIP URL for the account. The value can take name address or - * URL format, and will look something like "sip:account@serviceprovider". + * URL format, and will look something like "sip:account@serviceprovider" + * or "\"Display Name\" ". * * This field is mandatory. */ @@ -2743,6 +2906,15 @@ typedef struct pjsua_acc_config */ pj_str_t proxy[PJSUA_ACC_MAX_PROXIES]; + /** + * If remote sends SDP answer containing more than one format or codec in + * the media line, send re-INVITE or UPDATE with just one codec to lock + * which codec to use. + * + * Default: 1 (Yes). Set to zero to disable. + */ + unsigned lock_codec; + /** * Optional interval for registration, in seconds. If the value is zero, * default interval will be used (PJSUA_REG_INTERVAL, 300 seconds). @@ -2956,6 +3128,53 @@ typedef struct pjsua_acc_config */ pjsua_transport_config rtp_cfg; + /** + * Specify whether IPv6 should be used on media. + */ + pjsua_ipv6_use ipv6_media_use; + + /** + * Control the use of STUN for the SIP signaling. + * + * Default: PJSUA_STUN_USE_DEFAULT + */ + pjsua_stun_use sip_stun_use; + + /** + * Control the use of STUN for the media transports. + * + * Default: PJSUA_STUN_USE_DEFAULT + */ + pjsua_stun_use media_stun_use; + + /** + * Control the use of ICE in the account. By default, the settings in the + * \a pjsua_media_config will be used. + * + * Default: PJSUA_ICE_CONFIG_USE_DEFAULT + */ + pjsua_ice_config_use ice_cfg_use; + + /** + * The custom ICE setting for this account. This setting will only be + * used if \a ice_cfg_use is set to PJSUA_ICE_CONFIG_USE_CUSTOM + */ + pjsua_ice_config ice_cfg; + + /** + * Control the use of TURN in the account. By default, the settings in the + * \a pjsua_media_config will be used + * + * Default: PJSUA_TURN_CONFIG_USE_DEFAULT + */ + pjsua_turn_config_use turn_cfg_use; + + /** + * The custom TURN setting for this account. This setting will only be + * used if \a turn_cfg_use is set to PJSUA_TURN_CONFIG_USE_CUSTOM + */ + pjsua_turn_config turn_cfg; + /** * Specify whether secure media transport should be used for this account. * Valid values are PJMEDIA_SRTP_DISABLED, PJMEDIA_SRTP_OPTIONAL, and @@ -2963,7 +3182,7 @@ typedef struct pjsua_acc_config * * Default: #PJSUA_DEFAULT_USE_SRTP */ - pjmedia_srtp_use use_srtp; + pjmedia_srtp_use use_srtp; /** * Specify whether SRTP requires secure signaling to be used. This option @@ -3060,6 +3279,52 @@ typedef struct pjsua_acc_config } pjsua_acc_config; +/** + * Initialize ICE config from a media config. If the \a pool argument + * is NULL, a simple memcpy() will be used. + * + * @param pool Memory to duplicate strings. + * @param dst Destination config. + * @param src Source config. + */ +PJ_DECL(void) pjsua_ice_config_from_media_config(pj_pool_t *pool, + pjsua_ice_config *dst, + const pjsua_media_config *src); + +/** + * Clone. If the \a pool argument is NULL, a simple memcpy() will be used. + * + * @param pool Memory to duplicate strings. + * @param dst Destination config. + * @param src Source config. + */ +PJ_DECL(void) pjsua_ice_config_dup( pj_pool_t *pool, + pjsua_ice_config *dst, + const pjsua_ice_config *src); + +/** + * Initialize TURN config from a media config. If the \a pool argument + * is NULL, a simple memcpy() will be used. + * + * @param pool Memory to duplicate strings. + * @param dst Destination config. + * @param src Source config. + */ +PJ_DECL(void) pjsua_turn_config_from_media_config(pj_pool_t *pool, + pjsua_turn_config *dst, + const pjsua_media_config *src); + +/** + * Clone. If the \a pool argument is NULL, a simple memcpy() will be used. + * + * @param pool Memory to duplicate strings. + * @param dst Destination config. + * @param src Source config. + */ +PJ_DECL(void) pjsua_turn_config_dup(pj_pool_t *pool, + pjsua_turn_config *dst, + const pjsua_turn_config *src); + /** * Call this function to initialize account config with default values. * @@ -4291,7 +4556,8 @@ PJ_DECL(pj_status_t) pjsua_call_update2(pjsua_call_id call_id, * of the call transfer request. * * @param call_id The call id to be transfered. - * @param dest Address of new target to be contacted. + * @param dest URI of new target to be contacted. The URI may be + * in name address or addr-spec format. * @param msg_data Optional message components to be sent with * the request. * @@ -5014,6 +5280,28 @@ PJ_DECL(pj_status_t) pjsua_im_typing(pjsua_acc_id acc_id, #endif /* PJSUA_MEDIA_HAS_PJMEDIA */ +/** + * Specify whether the third party stream has the capability of retrieving + * the stream info, i.e: pjmedia_stream_get_info() and + * pjmedia_vid_stream_get_info(). Currently this capability is required + * by smart media update and call dump. + */ +#ifndef PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO +# define PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO 0 +#endif + + +/** + * Specify whether the third party stream has the capability of retrieving + * the stream statistics, i.e: pjmedia_stream_get_stat() and + * pjmedia_vid_stream_get_stat(). Currently this capability is required + * by call dump. + */ +#ifndef PJSUA_THIRD_PARTY_STREAM_HAS_GET_STAT +# define PJSUA_THIRD_PARTY_STREAM_HAS_GET_STAT 0 +#endif + + /** * Max ports in the conference bridge. This setting is the default value * for pjsua_media_config.max_media_ports. @@ -5080,6 +5368,14 @@ PJ_DECL(pj_status_t) pjsua_im_typing(pjsua_acc_id acc_id, #endif +/** + * Enable/disable "c=" line in SDP session level. Set to zero to disable it. + */ +#ifndef PJSUA_SDP_SESS_HAS_CONN +# define PJSUA_SDP_SESS_HAS_CONN 0 +#endif + + /** * This structure describes media configuration, which will be specified * when calling #pjsua_init(). Application MUST initialize this structure @@ -5275,6 +5571,16 @@ struct pjsua_media_config */ pj_bool_t ice_no_rtcp; + /** + * Send re-INVITE/UPDATE every after ICE connectivity check regardless + * the default ICE transport address is changed or not. When this is set + * to PJ_FALSE, re-INVITE/UPDATE will be sent only when the default ICE + * transport address is changed. + * + * Default: yes + */ + pj_bool_t ice_always_update; + /** * Enable TURN relay candidate in ICE. */ @@ -5319,6 +5625,20 @@ struct pjsua_media_config * Default: PJ_TRUE */ pj_bool_t vid_preview_enable_native; + + /** + * Disable smart media update (ticket #1568). The smart media update + * will check for any changes in the media properties after a successful + * SDP negotiation and the media will only be reinitialized when any + * change is found. When it is disabled, media streams will always be + * reinitialized after a successful SDP negotiation. + * + * Note for third party media, the smart media update requires stream info + * retrieval capability, see #PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO. + * + * Default: PJ_FALSE + */ + pj_bool_t no_smart_media_update; }; diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index c228513..3b38679 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -1,4 +1,4 @@ -/* $Id: pjsua_internal.h 4175 2012-06-22 08:53:11Z nanang $ */ +/* $Id: pjsua_internal.h 4342 2013-02-06 13:48:45Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -155,10 +155,7 @@ struct pjsua_call char last_text_buf_[128]; /**< Buffer for last_text. */ struct { - pj_timer_entry reinv_timer;/**< Reinvite retry timer. */ - pj_uint32_t sdp_ver; /**< SDP version of the bad answer */ int retry_cnt; /**< Retry count. */ - pj_bool_t pending; /**< Pending until CONFIRMED state */ } lock_codec; /**< Data for codec locking when answer contains multiple codecs. */ @@ -170,6 +167,7 @@ struct pjsua_call union { struct { pjsua_msg_data *msg_data;/**< Headers for outgoing INVITE. */ + pj_bool_t hangup; /**< Call is hangup? */ } out_call; struct { call_answer answers;/**< A list of call answers. */ @@ -184,6 +182,10 @@ struct pjsua_call offer. */ unsigned rem_vid_cnt; /**< No of active video in last remote offer. */ + + pj_timer_entry reinv_timer; /**< Reinvite retry timer. */ + pj_bool_t reinv_pending;/**< Pending until CONFIRMED state. */ + pj_bool_t reinv_ice_sent;/**< Has reinvite for ICE upd sent? */ }; @@ -336,6 +338,9 @@ typedef struct pjsua_stun_resolve PJ_DECL_LIST_MEMBER(struct pjsua_stun_resolve); pj_pool_t *pool; /**< Pool */ + int ref_cnt; /**< Reference count */ + pj_bool_t destroy_flag; /**< To be destroyed */ + pj_bool_t has_result; unsigned count; /**< # of entries */ pj_str_t *srv; /**< Array of entries */ unsigned idx; /**< Current index */ @@ -583,6 +588,20 @@ pj_status_t resolve_stun_server(pj_bool_t wait); */ pj_status_t normalize_route_uri(pj_pool_t *pool, pj_str_t *uri); +/* acc use stun? */ +pj_bool_t pjsua_sip_acc_is_using_stun(pjsua_acc_id acc_id); + +/* Get local transport address suitable to be used for Via or Contact address + * to send request to the specified destination URI. + */ +pj_status_t pjsua_acc_get_uac_addr(pjsua_acc_id acc_id, + pj_pool_t *pool, + const pj_str_t *dst_uri, + pjsip_host_port *addr, + pjsip_transport_type_e *p_tp_type, + int *p_secure, + const void **p_tp); + /** * Handle incoming invite request. */ @@ -798,6 +817,10 @@ PJ_DECL(void) pjsua_vid_win_reset(pjsua_vid_win_id wid); # define pjsua_vid_win_reset(wid) #endif +/* + * Schedule check for the need of re-INVITE/UPDATE after media update + */ +void pjsua_call_schedule_reinvite_check(pjsua_call *call, unsigned delay_ms); PJ_END_DECL diff --git a/pjsip/src/pjsip-simple/publishc.c b/pjsip/src/pjsip-simple/publishc.c index efee6fb..ab007ba 100644 --- a/pjsip/src/pjsip-simple/publishc.c +++ b/pjsip/src/pjsip-simple/publishc.c @@ -1,4 +1,4 @@ -/* $Id: publishc.c 4173 2012-06-20 10:39:05Z ming $ */ +/* $Id: publishc.c 4206 2012-07-16 02:45:09Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -355,8 +355,11 @@ PJ_DEF(pj_status_t) pjsip_publishc_set_via_sent_by(pjsip_publishc *pubc, if (!via_addr) pj_bzero(&pubc->via_addr, sizeof(pubc->via_addr)); - else - pubc->via_addr = *via_addr; + else { + if (pj_strcmp(&pubc->via_addr.host, &via_addr->host)) + pj_strdup(pubc->pool, &pubc->via_addr.host, &via_addr->host); + pubc->via_addr.port = via_addr->port; + } pubc->via_tp = via_tp; return PJ_SUCCESS; diff --git a/pjsip/src/pjsip-ua/sip_100rel.c b/pjsip/src/pjsip-ua/sip_100rel.c index 07122c4..f64ef19 100644 --- a/pjsip/src/pjsip-ua/sip_100rel.c +++ b/pjsip/src/pjsip-ua/sip_100rel.c @@ -1,4 +1,4 @@ -/* $Id: sip_100rel.c 3841 2011-10-24 09:28:13Z ming $ */ +/* $Id: sip_100rel.c 4208 2012-07-18 07:52:33Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -273,7 +273,7 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv, /* Find UAC state for the specified call leg */ uac_state = dd->uac_state_list; while (uac_state) { - if (pj_strcmp(&uac_state->tag, to_tag)==0) + if (pj_stricmp(&uac_state->tag, to_tag)==0) break; uac_state = uac_state->next; } @@ -320,7 +320,7 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv, /* If this response is a forked response from a different call-leg, * update the req URI (https://trac.pjsip.org/repos/ticket/1364) */ - if (pj_strcmp(&uac_state->tag, &dd->inv->dlg->remote.info->tag)) { + if (pj_stricmp(&uac_state->tag, &dd->inv->dlg->remote.info->tag)) { const pjsip_contact_hdr *mhdr; mhdr = (const pjsip_contact_hdr*) diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c index 8db5308..dec4ee5 100644 --- a/pjsip/src/pjsip-ua/sip_inv.c +++ b/pjsip/src/pjsip-ua/sip_inv.c @@ -1,4 +1,4 @@ -/* $Id: sip_inv.c 4156 2012-06-06 07:24:08Z bennylp $ */ +/* $Id: sip_inv.c 4367 2013-02-21 20:49:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -829,7 +829,7 @@ PJ_DEF(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata) sdp_info->body.slen, &sdp_info->sdp); if (status == PJ_SUCCESS) - status = pjmedia_sdp_validate(sdp_info->sdp); + status = pjmedia_sdp_validate2(sdp_info->sdp, PJ_FALSE); if (status != PJ_SUCCESS) { sdp_info->sdp = NULL; @@ -1745,7 +1745,7 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv, if (tsx->role == PJSIP_ROLE_UAC && rdata->msg_info.msg->line.status.code/100 == 2 && tsx_inv_data->done_early && - pj_strcmp(&tsx_inv_data->done_tag, &res_tag)) + pj_stricmp(&tsx_inv_data->done_tag, &res_tag)) { const pjmedia_sdp_session *reoffer_sdp = NULL; @@ -2329,6 +2329,7 @@ static pj_bool_t inv_uac_recurse(pjsip_inv_session *inv, int code, /* Check what the application wants to do now */ switch (op) { case PJSIP_REDIRECT_ACCEPT: + case PJSIP_REDIRECT_ACCEPT_REPLACE: case PJSIP_REDIRECT_STOP: /* Must increment session counter, that's the convention of the * pjsip_inv_process_redirect(). @@ -2394,6 +2395,7 @@ PJ_DEF(pj_status_t) pjsip_inv_process_redirect( pjsip_inv_session *inv, /* See what the application wants to do now */ switch (op) { case PJSIP_REDIRECT_ACCEPT: + case PJSIP_REDIRECT_ACCEPT_REPLACE: /* User accept the redirection. Reset the session and resend the * INVITE request. */ @@ -2419,6 +2421,51 @@ PJ_DEF(pj_status_t) pjsip_inv_process_redirect( pjsip_inv_session *inv, pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); via->branch_param.slen = 0; + /* Process PJSIP_REDIRECT_ACCEPT_REPLACE */ + if (op == PJSIP_REDIRECT_ACCEPT_REPLACE) { + pjsip_to_hdr *to; + pjsip_dialog *dlg = inv->dlg; + enum { TMP_LEN = 128 }; + char tmp[TMP_LEN]; + int len; + + /* Replace To header */ + to = PJSIP_MSG_TO_HDR(tdata->msg); + to->uri = (pjsip_uri*) + pjsip_uri_clone(tdata->pool, + dlg->target_set.current->uri); + to->tag.slen = 0; + pj_list_init(&to->other_param); + + /* Re-init dialog remote info */ + dlg->remote.info = (pjsip_to_hdr*) + pjsip_hdr_clone(dlg->pool, to); + + /* Remove header param from remote info */ + if (PJSIP_URI_SCHEME_IS_SIP(dlg->remote.info->uri) || + PJSIP_URI_SCHEME_IS_SIPS(dlg->remote.info->uri)) + { + pjsip_sip_uri *sip_uri = (pjsip_sip_uri *) + pjsip_uri_get_uri(dlg->remote.info->uri); + if (!pj_list_empty(&sip_uri->header_param)) { + /* Remove all header param */ + pj_list_init(&sip_uri->header_param); + } + } + + /* Print the remote info. */ + len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, + dlg->remote.info->uri, tmp, TMP_LEN); + if (len < 1) { + pj_ansi_strcpy(tmp, "<-error: uri too long->"); + len = pj_ansi_strlen(tmp); + } + pj_strdup2_with_null(dlg->pool, &dlg->remote.info_str, tmp); + + /* Secure? */ + dlg->secure = PJSIP_URI_SCHEME_IS_SIPS(to->uri); + } + /* Reset message destination info (see #1248). */ pj_bzero(&tdata->dest_info, sizeof(tdata->dest_info)); @@ -2572,6 +2619,7 @@ PJ_DEF(pj_status_t) pjsip_inv_update ( pjsip_inv_session *inv, pjsip_contact_hdr *contact_hdr = NULL; pjsip_tx_data *tdata = NULL; pjmedia_sdp_session *sdp_copy; + const pjsip_hdr *hdr; pj_status_t status = PJ_SUCCESS; /* Verify arguments. */ @@ -2640,13 +2688,24 @@ PJ_DEF(pj_status_t) pjsip_inv_update ( pjsip_inv_session *inv, pjsip_create_sdp_body(tdata->pool, sdp_copy, &tdata->msg->body); } - /* Unlock dialog. */ - pjsip_dlg_dec_lock(inv->dlg); + /* Session Timers spec (RFC 4028) says that Supported header MUST be put + * in refresh requests. So here we'll just put the Supported header in + * all cases regardless of whether session timers is used or not, just + * in case this is a common behavior. + */ + hdr = pjsip_endpt_get_capability(inv->dlg->endpt, PJSIP_H_SUPPORTED, NULL); + if (hdr) { + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) + pjsip_hdr_shallow_clone(tdata->pool, hdr)); + } status = pjsip_timer_update_req(inv, tdata); if (status != PJ_SUCCESS) goto on_error; + /* Unlock dialog. */ + pjsip_dlg_dec_lock(inv->dlg); + *p_tdata = tdata; pj_log_pop_indent(); @@ -2957,8 +3016,16 @@ static void inv_respond_incoming_update(pjsip_inv_session *inv, neg_state = pjmedia_sdp_neg_get_state(inv->neg); + /* If UPDATE doesn't contain SDP, just respond with 200/OK. + * This is a valid scenario according to session-timer draft. + */ + if (rdata->msg_info.msg->body == NULL) { + + status = pjsip_dlg_create_response(inv->dlg, rdata, + 200, NULL, &tdata); + } /* Send 491 if we receive UPDATE while we're waiting for an answer */ - if (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { + else if (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { status = pjsip_dlg_create_response(inv->dlg, rdata, PJSIP_SC_REQUEST_PENDING, NULL, &tdata); @@ -2967,18 +3034,18 @@ static void inv_respond_incoming_update(pjsip_inv_session *inv, * receive UPDATE while we haven't sent answer. */ else if (neg_state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER || - neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) { - status = pjsip_dlg_create_response(inv->dlg, rdata, + neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) + { + pjsip_retry_after_hdr *ra_hdr; + int val; + + status = pjsip_dlg_create_response(inv->dlg, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, &tdata); - /* If UPDATE doesn't contain SDP, just respond with 200/OK. - * This is a valid scenario according to session-timer draft. - */ - } else if (rdata->msg_info.msg->body == NULL) { - - status = pjsip_dlg_create_response(inv->dlg, rdata, - 200, NULL, &tdata); + val = (pj_rand() % 10); + ra_hdr = pjsip_retry_after_hdr_create(tdata->pool, val); + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ra_hdr); } else { /* We receive new offer from remote */ @@ -3305,8 +3372,7 @@ static pj_bool_t handle_uac_tsx_response(pjsip_inv_session *inv, ((tsx->status_code == PJSIP_SC_CALL_TSX_DOES_NOT_EXIST && tsx->method.id != PJSIP_CANCEL_METHOD) || tsx->status_code == PJSIP_SC_REQUEST_TIMEOUT || - tsx->status_code == PJSIP_SC_TSX_TIMEOUT || - tsx->status_code == PJSIP_SC_TSX_TRANSPORT_ERROR)) + tsx->status_code == PJSIP_SC_TSX_TIMEOUT)) { pjsip_tx_data *bye; pj_status_t status; diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c index bd1fb21..b76a1f6 100644 --- a/pjsip/src/pjsip-ua/sip_reg.c +++ b/pjsip/src/pjsip-ua/sip_reg.c @@ -1,4 +1,4 @@ -/* $Id: sip_reg.c 4173 2012-06-20 10:39:05Z ming $ */ +/* $Id: sip_reg.c 4319 2013-01-16 10:20:55Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -336,7 +336,7 @@ PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc, pj_status_t status; PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url && - contact_cnt && contact && expires, PJ_EINVAL); + expires, PJ_EINVAL); /* Copy server URL. */ pj_strdup_with_null(regc->pool, ®c->str_srv_url, srv_url); @@ -818,8 +818,11 @@ PJ_DEF(pj_status_t) pjsip_regc_set_via_sent_by( pjsip_regc *regc, if (!via_addr) pj_bzero(®c->via_addr, sizeof(regc->via_addr)); - else - regc->via_addr = *via_addr; + else { + if (pj_strcmp(®c->via_addr.host, &via_addr->host)) + pj_strdup(regc->pool, ®c->via_addr.host, &via_addr->host); + regc->via_addr.port = via_addr->port; + } regc->via_tp = via_tp; return PJ_SUCCESS; diff --git a/pjsip/src/pjsip-ua/sip_replaces.c b/pjsip/src/pjsip-ua/sip_replaces.c index b961cf9..a37546f 100644 --- a/pjsip/src/pjsip-ua/sip_replaces.c +++ b/pjsip/src/pjsip-ua/sip_replaces.c @@ -1,4 +1,4 @@ -/* $Id: sip_replaces.c 3999 2012-03-30 07:10:13Z bennylp $ */ +/* $Id: sip_replaces.c 4268 2012-09-28 08:56:08Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -305,10 +305,19 @@ PJ_DEF(pj_status_t) pjsip_replaces_verify_request( pjsip_rx_data *rdata, * initiated by this UA, it returns a 481 (Call/Transaction Does Not * Exist) response to the new INVITE. */ - if (inv->state <= PJSIP_INV_STATE_EARLY && inv->role != PJSIP_ROLE_UAC) { - code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST; - warn_text = "Found early INVITE session but not initiated by this UA"; - goto on_return; + if (inv->state <= PJSIP_INV_STATE_EARLY && inv->role != PJSIP_ROLE_UAC) + { + /* Really return 481 only if call haven't reached early state or + * accept-replace-in-early-state (ticket #1587) is not allowed. + */ + if (inv->state != PJSIP_INV_STATE_EARLY || + pjsip_cfg()->endpt.accept_replace_in_early_state == PJ_FALSE) + { + code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST; + warn_text = "Found early INVITE session but not initiated by " + "this UA"; + goto on_return; + } } diff --git a/pjsip/src/pjsip-ua/sip_timer.c b/pjsip/src/pjsip-ua/sip_timer.c index 2c70791..a48c71d 100644 --- a/pjsip/src/pjsip-ua/sip_timer.c +++ b/pjsip/src/pjsip-ua/sip_timer.c @@ -1,4 +1,4 @@ -/* $Id: sip_timer.c 3999 2012-03-30 07:10:13Z bennylp $ */ +/* $Id: sip_timer.c 4213 2012-07-23 13:31:26Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * @@ -1030,6 +1030,33 @@ PJ_DEF(pj_status_t) pjsip_timer_update_resp(pjsip_inv_session *inv, if (inv->timer && inv->timer->active) { /* Add Session-Expires header and start the timer */ add_timer_headers(inv, tdata, PJ_TRUE, PJ_FALSE); + + /* Add 'timer' to Require header (see ticket #1560). */ + if (inv->timer->refresher == TR_UAC) { + pjsip_require_hdr *req_hdr; + pj_bool_t req_hdr_has_timer = PJ_FALSE; + + req_hdr = (pjsip_require_hdr*) + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_REQUIRE, + NULL); + if (req_hdr == NULL) { + req_hdr = pjsip_require_hdr_create(tdata->pool); + PJ_ASSERT_RETURN(req_hdr, PJ_ENOMEM); + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)req_hdr); + } else { + unsigned i; + for (i = 0; i < req_hdr->count; ++i) { + if (pj_stricmp(&req_hdr->values[i], &STR_TIMER)) { + req_hdr_has_timer = PJ_TRUE; + break; + } + } + } + if (!req_hdr_has_timer) + req_hdr->values[req_hdr->count++] = STR_TIMER; + } + + /* Finally, start timer. */ start_timer(inv); } } diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c index ae850b1..0facc7b 100644 --- a/pjsip/src/pjsip/sip_auth_client.c +++ b/pjsip/src/pjsip/sip_auth_client.c @@ -1,4 +1,4 @@ -/* $Id: sip_auth_client.c 3999 2012-03-30 07:10:13Z bennylp $ */ +/* $Id: sip_auth_client.c 4322 2013-01-17 10:09:09Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -920,13 +920,13 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess, * or add an empty authorization header. */ unsigned i; - char *uri_str; - int len; + pj_str_t uri; - uri_str = (char*)pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE); - len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, tdata->msg->line.req.uri, - uri_str, PJSIP_MAX_URL_SIZE); - if (len < 1 || len >= PJSIP_MAX_URL_SIZE) + uri.ptr = (char*)pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE); + uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, + tdata->msg->line.req.uri, + uri.ptr, PJSIP_MAX_URL_SIZE); + if (uri.slen < 1 || uri.slen >= PJSIP_MAX_URL_SIZE) return PJSIP_EURITOOLONG; for (i=0; icred_cnt; ++i) { @@ -946,8 +946,7 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess, &c->username); pj_strdup(tdata->pool, &hs->credential.digest.realm, &c->realm); - pj_strdup2(tdata->pool, &hs->credential.digest.uri, - uri_str); + pj_strdup(tdata->pool, &hs->credential.digest.uri, &uri); pj_strdup(tdata->pool, &hs->credential.digest.algorithm, &sess->pref.algorithm); diff --git a/pjsip/src/pjsip/sip_auth_server.c b/pjsip/src/pjsip/sip_auth_server.c index 248e6cc..c38b40c 100644 --- a/pjsip/src/pjsip/sip_auth_server.c +++ b/pjsip/src/pjsip/sip_auth_server.c @@ -1,4 +1,4 @@ -/* $Id: sip_auth_server.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* $Id: sip_auth_server.c 4214 2012-07-25 14:29:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -40,6 +40,7 @@ PJ_DEF(pj_status_t) pjsip_auth_srv_init( pj_pool_t *pool, { PJ_ASSERT_RETURN(pool && auth_srv && realm && lookup, PJ_EINVAL); + pj_bzero(auth_srv, sizeof(*auth_srv)); pj_strdup( pool, &auth_srv->realm, realm); auth_srv->lookup = lookup; auth_srv->is_proxy = (options & PJSIP_AUTH_SRV_IS_PROXY); @@ -47,6 +48,26 @@ PJ_DEF(pj_status_t) pjsip_auth_srv_init( pj_pool_t *pool, return PJ_SUCCESS; } +/* + * Initialize server authorization session data structure to serve the + * specified realm and to use lookup_func function to look for the credential + * info. + */ +PJ_DEF(pj_status_t) pjsip_auth_srv_init2( + pj_pool_t *pool, + pjsip_auth_srv *auth_srv, + const pjsip_auth_srv_init_param *param) +{ + PJ_ASSERT_RETURN(pool && auth_srv && param, PJ_EINVAL); + + pj_bzero(auth_srv, sizeof(*auth_srv)); + pj_strdup( pool, &auth_srv->realm, param->realm); + auth_srv->lookup2 = param->lookup2; + auth_srv->is_proxy = (param->options & PJSIP_AUTH_SRV_IS_PROXY); + + return PJ_SUCCESS; +} + /* Verify incoming Authorization/Proxy-Authorization header against the * specified credential. @@ -148,11 +169,25 @@ PJ_DEF(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv, } /* Find the credential information for the account. */ - status = (*auth_srv->lookup)(rdata->tp_info.pool, &auth_srv->realm, - &acc_name, &cred_info); - if (status != PJ_SUCCESS) { - *status_code = PJSIP_SC_FORBIDDEN; - return status; + if (auth_srv->lookup2) { + pjsip_auth_lookup_cred_param param; + + pj_bzero(¶m, sizeof(param)); + param.realm = auth_srv->realm; + param.acc_name = acc_name; + param.rdata = rdata; + status = (*auth_srv->lookup2)(rdata->tp_info.pool, ¶m, &cred_info); + if (status != PJ_SUCCESS) { + *status_code = PJSIP_SC_FORBIDDEN; + return status; + } + } else { + status = (*auth_srv->lookup)(rdata->tp_info.pool, &auth_srv->realm, + &acc_name, &cred_info); + if (status != PJ_SUCCESS) { + *status_code = PJSIP_SC_FORBIDDEN; + return status; + } } /* Authenticate with the specified credential. */ diff --git a/pjsip/src/pjsip/sip_config.c b/pjsip/src/pjsip/sip_config.c index 8742c8a..92b8805 100644 --- a/pjsip/src/pjsip/sip_config.c +++ b/pjsip/src/pjsip/sip_config.c @@ -1,4 +1,4 @@ -/* $Id: sip_config.c 3999 2012-03-30 07:10:13Z bennylp $ */ +/* $Id: sip_config.c 4285 2012-10-19 04:23:57Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -26,6 +26,8 @@ pjsip_cfg_t pjsip_sip_cfg_var = /* Global settings */ { PJSIP_ALLOW_PORT_IN_FROMTO_HDR, + PJSIP_ACCEPT_REPLACE_IN_EARLY_STATE, + 0, 0, PJSIP_DONT_SWITCH_TO_TCP }, diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index 008cad2..0e70d33 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -1,4 +1,4 @@ -/* $Id: sip_dialog.c 4173 2012-06-20 10:39:05Z ming $ */ +/* $Id: sip_dialog.c 4208 2012-07-18 07:52:33Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -206,8 +206,8 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uac( pjsip_user_agent *ua, pj_create_unique_string(dlg->pool, &dlg->local.info->tag); /* Calculate hash value of local tag. */ - dlg->local.tag_hval = pj_hash_calc(0, dlg->local.info->tag.ptr, - dlg->local.info->tag.slen); + dlg->local.tag_hval = pj_hash_calc_tolower(0, NULL, + &dlg->local.info->tag); /* Randomize local CSeq. */ dlg->local.first_cseq = pj_rand() & 0x7FFF; @@ -374,8 +374,7 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua, pj_strdup(dlg->pool, &dlg->local.info_str, &tmp); /* Calculate hash value of local tag. */ - dlg->local.tag_hval = pj_hash_calc(0, dlg->local.info->tag.ptr, - dlg->local.info->tag.slen); + dlg->local.tag_hval = pj_hash_calc_tolower(0, NULL, &dlg->local.info->tag); /* Randomize local cseq */ @@ -522,8 +521,7 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua, ++dlg->tsx_count; /* Calculate hash value of remote tag. */ - dlg->remote.tag_hval = pj_hash_calc(0, dlg->remote.info->tag.ptr, - dlg->remote.info->tag.slen); + dlg->remote.tag_hval = pj_hash_calc_tolower(0, NULL, &dlg->remote.info->tag); /* Update remote capabilities info */ pjsip_dlg_update_remote_cap(dlg, rdata->msg_info.msg, PJ_TRUE); @@ -596,8 +594,11 @@ PJ_DEF(pj_status_t) pjsip_dlg_set_via_sent_by( pjsip_dialog *dlg, if (!via_addr) pj_bzero(&dlg->via_addr, sizeof(dlg->via_addr)); - else - dlg->via_addr = *via_addr; + else { + if (pj_strcmp(&dlg->via_addr.host, &via_addr->host)) + pj_strdup(dlg->pool, &dlg->via_addr.host, &via_addr->host); + dlg->via_addr.port = via_addr->port; + } dlg->via_tp = via_tp; return PJ_SUCCESS; @@ -1814,7 +1815,7 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ) res_code > 100 && res_code/100 <= 2 && pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) && - pj_strcmp(&dlg->remote.info->tag, &rdata->msg_info.to->tag))) + pj_stricmp(&dlg->remote.info->tag, &rdata->msg_info.to->tag))) { pjsip_contact_hdr *contact; @@ -1823,7 +1824,7 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ) * with To-tag or forking, apply strict update. */ pjsip_dlg_update_remote_cap(dlg, rdata->msg_info.msg, - pj_strcmp(&dlg->remote.info->tag, + pj_stricmp(&dlg->remote.info->tag, &rdata->msg_info.to->tag)); /* Update To tag. */ diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c index 2510d14..ae55990 100644 --- a/pjsip/src/pjsip/sip_endpoint.c +++ b/pjsip/src/pjsip/sip_endpoint.c @@ -1,4 +1,4 @@ -/* $Id: sip_endpoint.c 4154 2012-06-05 10:41:17Z bennylp $ */ +/* $Id: sip_endpoint.c 4411 2013-03-04 04:34:38Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -812,6 +812,104 @@ PJ_DEF(pj_timer_heap_t*) pjsip_endpt_get_timer_heap(pjsip_endpoint *endpt) return endpt->timer_heap; } +/* Init with default */ +PJ_DEF(void) pjsip_process_rdata_param_default(pjsip_process_rdata_param *p) +{ + pj_bzero(p, sizeof(*p)); +} + +/* Distribute rdata */ +PJ_DEF(pj_status_t) pjsip_endpt_process_rx_data( pjsip_endpoint *endpt, + pjsip_rx_data *rdata, + pjsip_process_rdata_param *p, + pj_bool_t *p_handled) +{ + pjsip_msg *msg; + pjsip_process_rdata_param def_prm; + pjsip_module *mod; + pj_bool_t handled = PJ_FALSE; + unsigned i; + pj_status_t status; + + PJ_ASSERT_RETURN(endpt && rdata, PJ_EINVAL); + + if (p==NULL) { + p = &def_prm; + pjsip_process_rdata_param_default(p); + } + + msg = rdata->msg_info.msg; + + if (p_handled) + *p_handled = PJ_FALSE; + + if (!p->silent) { + PJ_LOG(5, (THIS_FILE, "Distributing rdata to modules: %s", + pjsip_rx_data_get_info(rdata))); + pj_log_push_indent(); + } + + LOCK_MODULE_ACCESS(endpt); + + /* Find start module */ + if (p->start_mod) { + mod = (pjsip_module*) + pj_list_find_node(&endpt->module_list, p->start_mod); + if (!mod) { + status = PJ_ENOTFOUND; + goto on_return; + } + } else { + mod = endpt->module_list.next; + } + + /* Start after the specified index */ + for (i=0; i < p->idx_after_start && mod != &endpt->module_list; ++i) { + mod = mod->next; + } + + /* Start with the specified priority */ + while (mod != &endpt->module_list && mod->priority < (int)p->start_prio) { + mod = mod->next; + } + + if (mod == &endpt->module_list) { + status = PJ_ENOTFOUND; + goto on_return; + } + + /* Distribute */ + if (msg->type == PJSIP_REQUEST_MSG) { + do { + if (mod->on_rx_request) + handled = (*mod->on_rx_request)(rdata); + if (handled) + break; + mod = mod->next; + } while (mod != &endpt->module_list); + } else { + do { + if (mod->on_rx_response) + handled = (*mod->on_rx_response)(rdata); + if (handled) + break; + mod = mod->next; + } while (mod != &endpt->module_list); + } + + status = PJ_SUCCESS; + +on_return: + if (p_handled) + *p_handled = handled; + + UNLOCK_MODULE_ACCESS(endpt); + if (!p->silent) { + pj_log_pop_indent(); + } + return status; +} + /* * This is the callback that is called by the transport manager when it * receives a message from the network. @@ -820,7 +918,8 @@ static void endpt_on_rx_msg( pjsip_endpoint *endpt, pj_status_t status, pjsip_rx_data *rdata ) { - pjsip_msg *msg = rdata->msg_info.msg; + pjsip_process_rdata_param proc_prm; + pj_bool_t handled = PJ_FALSE; if (status != PJ_SUCCESS) { char info[30]; @@ -927,57 +1026,20 @@ static void endpt_on_rx_msg( pjsip_endpoint *endpt, } #endif + pjsip_process_rdata_param_default(&proc_prm); + proc_prm.silent = PJ_TRUE; - /* Distribute to modules, starting from modules with highest priority */ - LOCK_MODULE_ACCESS(endpt); - - if (msg->type == PJSIP_REQUEST_MSG) { - pjsip_module *mod; - pj_bool_t handled = PJ_FALSE; - - 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; - } - - /* No module is able to handle the request. */ - if (!handled) { - PJ_TODO(ENDPT_RESPOND_UNHANDLED_REQUEST); - PJ_LOG(4,(THIS_FILE, "Message %s from %s:%d was dropped/unhandled by" - " any modules", - pjsip_rx_data_get_info(rdata), - rdata->pkt_info.src_name, - rdata->pkt_info.src_port)); - } - - } else { - pjsip_module *mod; - pj_bool_t handled = PJ_FALSE; + pjsip_endpt_process_rx_data(endpt, rdata, &proc_prm, &handled); - 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 (!handled) { - PJ_LOG(4,(THIS_FILE, "Message %s from %s:%d was dropped/unhandled" - " by any modules", - pjsip_rx_data_get_info(rdata), - rdata->pkt_info.src_name, - rdata->pkt_info.src_port)); - } + /* No module is able to handle the message */ + if (!handled) { + PJ_LOG(4,(THIS_FILE, "%s from %s:%d was dropped/unhandled by" + " any modules", + pjsip_rx_data_get_info(rdata), + rdata->pkt_info.src_name, + rdata->pkt_info.src_port)); } - UNLOCK_MODULE_ACCESS(endpt); - /* Must clear mod_data before returning rdata to transport, since * rdata may be reused. */ diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c index 2d46fe9..26f4aca 100644 --- a/pjsip/src/pjsip/sip_parser.c +++ b/pjsip/src/pjsip/sip_parser.c @@ -1,4 +1,4 @@ -/* $Id: sip_parser.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* $Id: sip_parser.c 4288 2012-10-26 09:30:37Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -1522,8 +1522,15 @@ static pjsip_name_addr *int_parse_name_addr( pj_scanner *scanner, /* Get the SIP-URL */ has_bracket = (*scanner->curptr == '<'); - if (has_bracket) + if (has_bracket) { pj_scan_get_char(scanner); + } else if (name_addr->display.slen) { + /* Must have bracket now (2012-10-26). + * Allowing (invalid) name-addr to pass URI verification will + * cause us to send invalid URI to the wire. + */ + PJ_THROW( PJSIP_SYN_ERR_EXCEPTION); + } name_addr->uri = int_parse_uri( scanner, pool, PJ_TRUE ); if (has_bracket) { if (pj_scan_get_char(scanner) != '>') diff --git a/pjsip/src/pjsip/sip_tel_uri.c b/pjsip/src/pjsip/sip_tel_uri.c index 4120ae0..b98db57 100644 --- a/pjsip/src/pjsip/sip_tel_uri.c +++ b/pjsip/src/pjsip/sip_tel_uri.c @@ -1,4 +1,4 @@ -/* $Id: sip_tel_uri.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* $Id: sip_tel_uri.c 4322 2013-01-17 10:09:09Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -182,7 +182,7 @@ static pj_ssize_t tel_uri_print( pjsip_uri_context_e context, { int printed; char *startbuf = buf; - char *endbuf = buf+size; + char *endbuf = buf+size-1; const pjsip_parser_const_t *pc = pjsip_parser_const(); PJ_UNUSED_ARG(context); @@ -217,6 +217,8 @@ static pj_ssize_t tel_uri_print( pjsip_uri_context_e context, return -1; buf += printed; + *buf = '\0'; + return (buf-startbuf); } diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c index 4b3dd12..1321d9f 100644 --- a/pjsip/src/pjsip/sip_transaction.c +++ b/pjsip/src/pjsip/sip_transaction.c @@ -1,4 +1,4 @@ -/* $Id: sip_transaction.c 4165 2012-06-14 09:04:20Z bennylp $ */ +/* $Id: sip_transaction.c 4208 2012-07-18 07:52:33Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -403,7 +403,7 @@ PJ_DEF(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *key, */ const pj_str_t *branch = &rdata->msg_info.via->branch_param; - if (pj_strncmp(branch,&rfc3261_branch,PJSIP_RFC3261_BRANCH_LEN)==0) { + if (pj_strnicmp(branch,&rfc3261_branch,PJSIP_RFC3261_BRANCH_LEN)==0) { /* Create transaction key. */ return create_tsx_key_3261(pool, key, role, method, branch); @@ -548,10 +548,10 @@ static pj_status_t mod_tsx_layer_register_tsx( pjsip_transaction *tsx) * Do not use PJ_ASSERT_RETURN since it evaluates the expression * twice! */ - if(pj_hash_get(mod_tsx_layer.htable, - tsx->transaction_key.ptr, - tsx->transaction_key.slen, - NULL)) + if(pj_hash_get_lower(mod_tsx_layer.htable, + tsx->transaction_key.ptr, + tsx->transaction_key.slen, + NULL)) { pj_mutex_unlock(mod_tsx_layer.mutex); PJ_LOG(2,(THIS_FILE, @@ -568,11 +568,13 @@ static pj_status_t mod_tsx_layer_register_tsx( pjsip_transaction *tsx) /* Register the transaction to the hash table. */ #ifdef PRECALC_HASH - pj_hash_set( tsx->pool, mod_tsx_layer.htable, tsx->transaction_key.ptr, - tsx->transaction_key.slen, tsx->hashed_key, tsx); + pj_hash_set_lower( tsx->pool, mod_tsx_layer.htable, + tsx->transaction_key.ptr, + tsx->transaction_key.slen, tsx->hashed_key, tsx); #else - pj_hash_set( tsx->pool, mod_tsx_layer.htable, tsx->transaction_key.ptr, - tsx->transaction_key.slen, 0, tsx); + pj_hash_set_lower( tsx->pool, mod_tsx_layer.htable, + tsx->transaction_key.ptr, + tsx->transaction_key.slen, 0, tsx); #endif /* Unlock mutex. */ @@ -604,11 +606,11 @@ static void mod_tsx_layer_unregister_tsx( pjsip_transaction *tsx) /* Register the transaction to the hash table. */ #ifdef PRECALC_HASH - pj_hash_set( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr, - tsx->transaction_key.slen, tsx->hashed_key, NULL); + pj_hash_set_lower( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr, + tsx->transaction_key.slen, tsx->hashed_key, NULL); #else - pj_hash_set( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr, - tsx->transaction_key.slen, 0, NULL); + pj_hash_set_lower( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr, + tsx->transaction_key.slen, 0, NULL); #endif TSX_TRACE_((THIS_FILE, @@ -651,7 +653,8 @@ PJ_DEF(pjsip_transaction*) pjsip_tsx_layer_find_tsx( const pj_str_t *key, pj_mutex_lock(mod_tsx_layer.mutex); tsx = (pjsip_transaction*) - pj_hash_get( mod_tsx_layer.htable, key->ptr, key->slen, &hval ); + pj_hash_get_lower( mod_tsx_layer.htable, key->ptr, key->slen, + &hval ); pj_mutex_unlock(mod_tsx_layer.mutex); TSX_TRACE_((THIS_FILE, @@ -785,7 +788,7 @@ static pj_bool_t mod_tsx_layer_on_rx_request(pjsip_rx_data *rdata) pj_mutex_lock( mod_tsx_layer.mutex ); tsx = (pjsip_transaction*) - pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen, &hval ); + pj_hash_get_lower( mod_tsx_layer.htable, key.ptr, key.slen, &hval ); TSX_TRACE_((THIS_FILE, @@ -834,7 +837,7 @@ static pj_bool_t mod_tsx_layer_on_rx_response(pjsip_rx_data *rdata) pj_mutex_lock( mod_tsx_layer.mutex ); tsx = (pjsip_transaction*) - pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen, &hval ); + pj_hash_get_lower( mod_tsx_layer.htable, key.ptr, key.slen, &hval ); TSX_TRACE_((THIS_FILE, @@ -1299,8 +1302,7 @@ PJ_DEF(pj_status_t) pjsip_tsx_create_uac( pjsip_module *tsx_user, /* Calculate hashed key value. */ #ifdef PRECALC_HASH - tsx->hashed_key = pj_hash_calc(0, tsx->transaction_key.ptr, - tsx->transaction_key.slen); + tsx->hashed_key = pj_hash_calc_tolower(0, NULL, &tsx->transaction_key); #endif PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen, @@ -1432,8 +1434,7 @@ PJ_DEF(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user, /* Calculate hashed key value. */ #ifdef PRECALC_HASH - tsx->hashed_key = pj_hash_calc(0, tsx->transaction_key.ptr, - tsx->transaction_key.slen); + tsx->hashed_key = pj_hash_calc_tolower(0, NULL, &tsx->transaction_key); #endif /* Duplicate branch parameter for transaction. */ diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index 4d8b77e..1598241 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -1,4 +1,4 @@ -/* $Id: sip_transport.c 4094 2012-04-26 09:31:00Z bennylp $ */ +/* $Id: sip_transport.c 4295 2012-11-06 05:22:11Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -197,6 +198,13 @@ struct transport_names_t "TCP IPv6 transport", PJSIP_TRANSPORT_RELIABLE }, + { + PJSIP_TRANSPORT_TLS6, + 5061, + {"TLS", 3}, + "TLS IPv6 transport", + PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE + }, }; static void tp_state_callback(pjsip_transport *tp, @@ -594,6 +602,89 @@ PJ_DEF(char*) pjsip_rx_data_get_info(pjsip_rx_data *rdata) return rdata->msg_info.info; } +/* Clone pjsip_rx_data. */ +PJ_DEF(pj_status_t) pjsip_rx_data_clone( const pjsip_rx_data *src, + unsigned flags, + pjsip_rx_data **p_rdata) +{ + pj_pool_t *pool; + pjsip_rx_data *dst; + pjsip_hdr *hdr; + + PJ_ASSERT_RETURN(src && flags==0 && p_rdata, PJ_EINVAL); + + pool = pj_pool_create(src->tp_info.pool->factory, + "rtd%p", + PJSIP_POOL_RDATA_LEN, + PJSIP_POOL_RDATA_INC, + NULL); + if (!pool) + return PJ_ENOMEM; + + dst = PJ_POOL_ZALLOC_T(pool, pjsip_rx_data); + + /* Parts of tp_info */ + dst->tp_info.pool = pool; + dst->tp_info.transport = (pjsip_transport*)src->tp_info.transport; + + /* pkt_info can be memcopied */ + pj_memcpy(&dst->pkt_info, &src->pkt_info, sizeof(src->pkt_info)); + + /* msg_info needs deep clone */ + dst->msg_info.msg_buf = dst->pkt_info.packet; + dst->msg_info.len = src->msg_info.len; + dst->msg_info.msg = pjsip_msg_clone(pool, src->msg_info.msg); + pj_list_init(&dst->msg_info.parse_err); + +#define GET_MSG_HDR2(TYPE, type, var) \ + case PJSIP_H_##TYPE: \ + if (!dst->msg_info.var) { \ + dst->msg_info.var = (pjsip_##type##_hdr*)hdr; \ + } \ + break +#define GET_MSG_HDR(TYPE, var_type) GET_MSG_HDR2(TYPE, var_type, var_type) + + hdr = dst->msg_info.msg->hdr.next; + while (hdr != &dst->msg_info.msg->hdr) { + switch (hdr->type) { + GET_MSG_HDR(CALL_ID, cid); + GET_MSG_HDR(FROM, from); + GET_MSG_HDR(TO, to); + GET_MSG_HDR(VIA, via); + GET_MSG_HDR(CSEQ, cseq); + GET_MSG_HDR(MAX_FORWARDS, max_fwd); + GET_MSG_HDR(ROUTE, route); + GET_MSG_HDR2(RECORD_ROUTE, rr, record_route); + GET_MSG_HDR(CONTENT_TYPE, ctype); + GET_MSG_HDR(CONTENT_LENGTH, clen); + GET_MSG_HDR(REQUIRE, require); + GET_MSG_HDR(SUPPORTED, supported); + default: + break; + } + hdr = hdr->next; + } + +#undef GET_MSG_HDR +#undef GET_MSG_HDR2 + + *p_rdata = dst; + + /* Finally add transport ref */ + return pjsip_transport_add_ref(dst->tp_info.transport); +} + +/* Free previously cloned pjsip_rx_data. */ +PJ_DEF(pj_status_t) pjsip_rx_data_free_cloned(pjsip_rx_data *rdata) +{ + PJ_ASSERT_RETURN(rdata, PJ_EINVAL); + + pjsip_transport_dec_ref(rdata->tp_info.transport); + pj_pool_release(rdata->tp_info.pool); + + return PJ_SUCCESS; +} + /***************************************************************************** * * TRANSPORT KEY @@ -1080,6 +1171,10 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_unregister_tpfactory( pjsip_tpmgr *mgr, return PJ_SUCCESS; } +PJ_DECL(void) pjsip_tpmgr_fla2_param_default(pjsip_tpmgr_fla2_param *prm) +{ + pj_bzero(prm, sizeof(*prm)); +} /***************************************************************************** * @@ -1138,7 +1233,27 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool, return PJ_SUCCESS; } +/* Get the interface to send packet to the specified address */ +static pj_status_t get_net_interface(pjsip_transport_type_e tp_type, + const pj_str_t *dst, + pj_str_t *itf_str_addr) +{ + int af; + pj_sockaddr itf_addr; + pj_status_t status; + + af = (tp_type & PJSIP_TRANSPORT_IPV6)? PJ_AF_INET6 : PJ_AF_INET; + status = pj_getipinterface(af, dst, &itf_addr, PJ_FALSE, NULL); + if (status != PJ_SUCCESS) + return status; + /* Print address */ + pj_sockaddr_print(&itf_addr, itf_str_addr->ptr, + PJ_INET6_ADDRSTRLEN, 0); + itf_str_addr->slen = pj_ansi_strlen(itf_str_addr->ptr); + + return PJ_SUCCESS; +} /* * Find out the appropriate local address info (IP address and port) to @@ -1149,46 +1264,66 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool, * In this implementation, it will only select the transport based on * the transport type in the request. */ -PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr, +PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr2(pjsip_tpmgr *tpmgr, pj_pool_t *pool, - pjsip_transport_type_e type, - const pjsip_tpselector *sel, - pj_str_t *ip_addr, - int *port) + pjsip_tpmgr_fla2_param *prm) { + char tmp_buf[PJ_INET6_ADDRSTRLEN+10]; + pj_str_t tmp_str; pj_status_t status = PJSIP_EUNSUPTRANSPORT; unsigned flag; /* Sanity checks */ - PJ_ASSERT_RETURN(tpmgr && pool && ip_addr && port, PJ_EINVAL); + PJ_ASSERT_RETURN(tpmgr && pool && prm, PJ_EINVAL); - ip_addr->slen = 0; - *port = 0; + pj_strset(&tmp_str, tmp_buf, 0); + prm->ret_addr.slen = 0; + prm->ret_port = 0; + prm->ret_tp = NULL; - flag = pjsip_transport_get_flag_from_type(type); + flag = pjsip_transport_get_flag_from_type(prm->tp_type); - if (sel && sel->type == PJSIP_TPSELECTOR_TRANSPORT && - sel->u.transport) + if (prm->tp_sel && prm->tp_sel->type == PJSIP_TPSELECTOR_TRANSPORT && + prm->tp_sel->u.transport) { - pj_strdup(pool, ip_addr, &sel->u.transport->local_name.host); - *port = sel->u.transport->local_name.port; + const pjsip_transport *tp = prm->tp_sel->u.transport; + if (prm->local_if) { + status = get_net_interface((pjsip_transport_type_e)tp->key.type, + &prm->dst_host, &tmp_str); + if (status != PJ_SUCCESS) + goto on_return; + pj_strdup(pool, &prm->ret_addr, &tmp_str); + prm->ret_port = pj_sockaddr_get_port(&tp->local_addr); + prm->ret_tp = tp; + } else { + pj_strdup(pool, &prm->ret_addr, &tp->local_name.host); + prm->ret_port = (pj_uint16_t)tp->local_name.port; + } status = PJ_SUCCESS; - } else if (sel && sel->type == PJSIP_TPSELECTOR_LISTENER && - sel->u.listener) + } else if (prm->tp_sel && prm->tp_sel->type == PJSIP_TPSELECTOR_LISTENER && + prm->tp_sel->u.listener) { - pj_strdup(pool, ip_addr, &sel->u.listener->addr_name.host); - *port = sel->u.listener->addr_name.port; + if (prm->local_if) { + status = get_net_interface(prm->tp_sel->u.listener->type, + &prm->dst_host, &tmp_str); + if (status != PJ_SUCCESS) + goto on_return; + pj_strdup(pool, &prm->ret_addr, &tmp_str); + } else { + pj_strdup(pool, &prm->ret_addr, + &prm->tp_sel->u.listener->addr_name.host); + } + prm->ret_port = (pj_uint16_t)prm->tp_sel->u.listener->addr_name.port; status = PJ_SUCCESS; } else if ((flag & PJSIP_TRANSPORT_DATAGRAM) != 0) { - pj_sockaddr remote; int addr_len; pjsip_transport *tp; pj_bzero(&remote, sizeof(remote)); - if (type & PJSIP_TRANSPORT_IPV6) { + if (prm->tp_type & PJSIP_TRANSPORT_IPV6) { addr_len = sizeof(pj_sockaddr_in6); remote.addr.sa_family = pj_AF_INET6(); } else { @@ -1196,13 +1331,23 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr, remote.addr.sa_family = pj_AF_INET(); } - status = pjsip_tpmgr_acquire_transport(tpmgr, type, &remote, + status = pjsip_tpmgr_acquire_transport(tpmgr, prm->tp_type, &remote, addr_len, NULL, &tp); if (status == PJ_SUCCESS) { - pj_strdup(pool, ip_addr, &tp->local_name.host); - *port = tp->local_name.port; - status = PJ_SUCCESS; + if (prm->local_if) { + status = get_net_interface((pjsip_transport_type_e) + tp->key.type, + &prm->dst_host, &tmp_str); + if (status != PJ_SUCCESS) + goto on_return; + pj_strdup(pool, &prm->ret_addr, &tmp_str); + prm->ret_port = pj_sockaddr_get_port(&tp->local_addr); + prm->ret_tp = tp; + } else { + pj_strdup(pool, &prm->ret_addr, &tp->local_name.host); + prm->ret_port = (pj_uint16_t)tp->local_name.port; + } pjsip_transport_dec_ref(tp); } @@ -1215,22 +1360,66 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr, f = tpmgr->factory_list.next; while (f != &tpmgr->factory_list) { - if (f->type == type) + if (f->type == prm->tp_type) break; f = f->next; } if (f != &tpmgr->factory_list) { - pj_strdup(pool, ip_addr, &f->addr_name.host); - *port = f->addr_name.port; + if (prm->local_if) { + status = get_net_interface(f->type, &prm->dst_host, + &tmp_str); + if (status == PJ_SUCCESS) { + pj_strdup(pool, &prm->ret_addr, &tmp_str); + } else { + /* It could fail "normally" on certain cases, e.g. + * when connecting to IPv6 link local address, it + * will wail with EINVAL. + * In this case, fallback to use the default interface + * rather than failing the call. + */ + PJ_PERROR(5,(THIS_FILE, status, "Warning: unable to " + "determine local interface")); + pj_strdup(pool, &prm->ret_addr, &f->addr_name.host); + status = PJ_SUCCESS; + } + } else { + pj_strdup(pool, &prm->ret_addr, &f->addr_name.host); + } + prm->ret_port = (pj_uint16_t)f->addr_name.port; status = PJ_SUCCESS; } pj_lock_release(tpmgr->lock); } +on_return: return status; } +PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr, + pj_pool_t *pool, + pjsip_transport_type_e type, + const pjsip_tpselector *sel, + pj_str_t *ip_addr, + int *port) +{ + pjsip_tpmgr_fla2_param prm; + pj_status_t status; + + pjsip_tpmgr_fla2_param_default(&prm); + prm.tp_type = type; + prm.tp_sel = sel; + + status = pjsip_tpmgr_find_local_addr2(tpmgr, pool, &prm); + if (status != PJ_SUCCESS) + return status; + + *ip_addr = prm.ret_addr; + *port = prm.ret_port; + + return PJ_SUCCESS; +} + /* * Return number of transports currently registered to the transport * manager. diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c index 141434b..808cee9 100644 --- a/pjsip/src/pjsip/sip_transport_tcp.c +++ b/pjsip/src/pjsip/sip_transport_tcp.c @@ -1,4 +1,4 @@ -/* $Id: sip_transport_tcp.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* $Id: sip_transport_tcp.c 4294 2012-11-06 05:02:10Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -57,6 +57,7 @@ struct tcp_listener pjsip_endpoint *endpt; pjsip_tpmgr *tpmgr; pj_activesock_t *asock; + pj_sockaddr bound_addr; pj_qos_type qos_type; pj_qos_params qos_params; }; @@ -73,6 +74,7 @@ struct delayed_tdata { PJ_DECL_LIST_MEMBER(struct delayed_tdata); pjsip_tx_data_op_key *tdata_op_key; + pj_time_val timeout; }; @@ -140,8 +142,8 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, static pj_status_t tcp_create(struct tcp_listener *listener, pj_pool_t *pool, pj_sock_t sock, pj_bool_t is_server, - const pj_sockaddr_in *local, - const pj_sockaddr_in *remote, + const pj_sockaddr *local, + const pj_sockaddr *remote, struct tcp_transport **p_tcp); @@ -158,10 +160,10 @@ static void tcp_perror(const char *sender, const char *title, static void sockaddr_to_host_port( pj_pool_t *pool, pjsip_host_port *host_port, - const pj_sockaddr_in *addr ) + const pj_sockaddr *addr ) { host_port->host.ptr = (char*) pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+4); - pj_sockaddr_print(addr, host_port->host.ptr, PJ_INET6_ADDRSTRLEN+4, 2); + pj_sockaddr_print(addr, host_port->host.ptr, PJ_INET6_ADDRSTRLEN+4, 0); host_port->host.slen = pj_ansi_strlen(host_port->host.ptr); host_port->port = pj_sockaddr_get_port(addr); } @@ -266,17 +268,21 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3( listener = PJ_POOL_ZALLOC_T(pool, struct tcp_listener); listener->factory.pool = pool; - listener->factory.type = PJSIP_TRANSPORT_TCP; - listener->factory.type_name = "tcp"; + listener->factory.type = cfg->af==pj_AF_INET() ? PJSIP_TRANSPORT_TCP : + PJSIP_TRANSPORT_TCP6; + listener->factory.type_name = (char*) + pjsip_transport_get_type_name(listener->factory.type); listener->factory.flag = - pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TCP); + pjsip_transport_get_flag_from_type(listener->factory.type); listener->qos_type = cfg->qos_type; pj_memcpy(&listener->qos_params, &cfg->qos_params, sizeof(cfg->qos_params)); pj_ansi_strcpy(listener->factory.obj_name, "tcplis"); + if (listener->factory.type==PJSIP_TRANSPORT_TCP6) + pj_ansi_strcat(listener->factory.obj_name, "6"); - status = pj_lock_create_recursive_mutex(pool, "tcplis", + status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name, &listener->factory.lock); if (status != PJ_SUCCESS) goto on_error; @@ -292,6 +298,11 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3( 2, listener->factory.obj_name, "SIP TCP listener socket"); + /* Bind address may be different than factory.local_addr because + * factory.local_addr will be resolved below. + */ + pj_sockaddr_cp(&listener->bound_addr, &cfg->bind_addr); + /* Bind socket */ listener_addr = &listener->factory.local_addr; pj_sockaddr_cp(listener_addr, &cfg->bind_addr); @@ -326,19 +337,18 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3( if (!pj_sockaddr_has_addr(listener_addr)) { pj_sockaddr hostip; - status = pj_gethostip(pj_AF_INET(), &hostip); + status = pj_gethostip(listener->bound_addr.addr.sa_family, + &hostip); if (status != PJ_SUCCESS) goto on_error; - pj_memcpy(pj_sockaddr_get_addr(listener_addr), - pj_sockaddr_get_addr(&hostip), - pj_sockaddr_get_addr_len(&hostip)); + pj_sockaddr_copy_addr(listener_addr, &hostip); } /* Save the address name */ sockaddr_to_host_port(listener->factory.pool, &listener->factory.addr_name, - (pj_sockaddr_in*)listener_addr); + listener_addr); } /* If port is zero, get the bound port */ @@ -535,8 +545,8 @@ static void tcp_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e); static pj_status_t tcp_create( struct tcp_listener *listener, pj_pool_t *pool, pj_sock_t sock, pj_bool_t is_server, - const pj_sockaddr_in *local, - const pj_sockaddr_in *remote, + const pj_sockaddr *local, + const pj_sockaddr *remote, struct tcp_transport **p_tcp) { struct tcp_transport *tcp; @@ -544,6 +554,7 @@ static pj_status_t tcp_create( struct tcp_listener *listener, pj_activesock_cfg asock_cfg; pj_activesock_cb tcp_callback; const pj_str_t ka_pkt = PJSIP_TCP_KEEP_ALIVE_DATA; + char print_addr[PJ_INET6_ADDRSTRLEN+10]; pj_status_t status; @@ -579,18 +590,21 @@ static pj_status_t tcp_create( struct tcp_listener *listener, goto on_error; } - tcp->base.key.type = PJSIP_TRANSPORT_TCP; - pj_memcpy(&tcp->base.key.rem_addr, remote, sizeof(pj_sockaddr_in)); - tcp->base.type_name = "tcp"; - tcp->base.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TCP); + tcp->base.key.type = listener->factory.type; + pj_sockaddr_cp(&tcp->base.key.rem_addr, remote); + tcp->base.type_name = (char*)pjsip_transport_get_type_name( + (pjsip_transport_type_e)tcp->base.key.type); + tcp->base.flag = pjsip_transport_get_flag_from_type( + (pjsip_transport_type_e)tcp->base.key.type); tcp->base.info = (char*) pj_pool_alloc(pool, 64); - pj_ansi_snprintf(tcp->base.info, 64, "TCP to %s:%d", - pj_inet_ntoa(remote->sin_addr), - (int)pj_ntohs(remote->sin_port)); + pj_ansi_snprintf(tcp->base.info, 64, "%s to %s", + tcp->base.type_name, + pj_sockaddr_print(remote, print_addr, + sizeof(print_addr), 3)); - tcp->base.addr_len = sizeof(pj_sockaddr_in); - pj_memcpy(&tcp->base.local_addr, local, sizeof(pj_sockaddr_in)); + tcp->base.addr_len = pj_sockaddr_get_len(remote); + pj_sockaddr_cp(&tcp->base.local_addr, local); sockaddr_to_host_port(pool, &tcp->base.local_name, local); sockaddr_to_host_port(pool, &tcp->base.remote_name, remote); tcp->base.dir = is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING; @@ -601,7 +615,6 @@ static pj_status_t tcp_create( struct tcp_listener *listener, tcp->base.do_shutdown = &tcp_shutdown; tcp->base.destroy = &tcp_destroy_transport; - /* Create active socket */ pj_activesock_cfg_default(&asock_cfg); asock_cfg.async_cnt = 1; @@ -649,6 +662,9 @@ on_error: /* Flush all delayed transmision once the socket is connected. */ static void tcp_flush_pending_tx(struct tcp_transport *tcp) { + pj_time_val now; + + pj_gettickcount(&now); pj_lock_acquire(tcp->base.lock); while (!pj_list_empty(&tcp->delayed_list)) { struct delayed_tdata *pending_tx; @@ -663,12 +679,20 @@ static void tcp_flush_pending_tx(struct tcp_transport *tcp) tdata = pending_tx->tdata_op_key->tdata; op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key; + if (pending_tx->timeout.sec > 0 && + PJ_TIME_VAL_GT(now, pending_tx->timeout)) + { + continue; + } + /* send! */ size = tdata->buf.cur - tdata->buf.start; status = pj_activesock_send(tcp->asock, op_key, tdata->buf.start, &size, 0); if (status != PJ_EPENDING) { + pj_lock_release(tcp->base.lock); on_data_sent(tcp->asock, op_key, size); + pj_lock_acquire(tcp->base.lock); } } @@ -791,7 +815,7 @@ static pj_status_t tcp_start_read(struct tcp_transport *tcp) { pj_pool_t *pool; pj_ssize_t size; - pj_sockaddr_in *rem_addr; + pj_sockaddr *rem_addr; void *readbuf[1]; pj_status_t status; @@ -814,11 +838,11 @@ static pj_status_t tcp_start_read(struct tcp_transport *tcp) sizeof(pj_ioqueue_op_key_t)); tcp->rdata.pkt_info.src_addr = tcp->base.key.rem_addr; - tcp->rdata.pkt_info.src_addr_len = sizeof(pj_sockaddr_in); - rem_addr = (pj_sockaddr_in*) &tcp->base.key.rem_addr; - pj_ansi_strcpy(tcp->rdata.pkt_info.src_name, - pj_inet_ntoa(rem_addr->sin_addr)); - tcp->rdata.pkt_info.src_port = pj_ntohs(rem_addr->sin_port); + tcp->rdata.pkt_info.src_addr_len = sizeof(tcp->rdata.pkt_info.src_addr); + rem_addr = &tcp->base.key.rem_addr; + pj_sockaddr_print(rem_addr, tcp->rdata.pkt_info.src_name, + sizeof(tcp->rdata.pkt_info.src_name), 0); + tcp->rdata.pkt_info.src_port = pj_sockaddr_get_port(rem_addr); size = sizeof(tcp->rdata.pkt_info.packet); readbuf[0] = tcp->rdata.pkt_info.packet; @@ -848,23 +872,25 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, struct tcp_listener *listener; struct tcp_transport *tcp; pj_sock_t sock; - pj_sockaddr_in local_addr; + pj_sockaddr local_addr; pj_status_t status; /* Sanity checks */ PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr && addr_len && p_transport, PJ_EINVAL); - /* Check that address is a sockaddr_in */ - PJ_ASSERT_RETURN(rem_addr->addr.sa_family == pj_AF_INET() && - addr_len == sizeof(pj_sockaddr_in), PJ_EINVAL); + /* Check that address is a sockaddr_in or sockaddr_in6*/ + PJ_ASSERT_RETURN((rem_addr->addr.sa_family == pj_AF_INET() && + addr_len == sizeof(pj_sockaddr_in)) || + (rem_addr->addr.sa_family == pj_AF_INET6() && + addr_len == sizeof(pj_sockaddr_in6)), PJ_EINVAL); listener = (struct tcp_listener*)factory; - /* Create socket */ - status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock); + status = pj_sock_socket(rem_addr->addr.sa_family, pj_SOCK_STREAM(), + 0, &sock); if (status != PJ_SUCCESS) return status; @@ -874,15 +900,20 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, 2, listener->factory.obj_name, "outgoing SIP TCP socket"); - /* Bind to any port */ - status = pj_sock_bind_in(sock, 0, 0); + /* Bind to listener's address and any port */ + pj_bzero(&local_addr, sizeof(local_addr)); + pj_sockaddr_cp(&local_addr, &listener->bound_addr); + pj_sockaddr_set_port(&local_addr, 0); + + status = pj_sock_bind(sock, &local_addr, + pj_sockaddr_get_len(&local_addr)); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Get the local port */ - addr_len = sizeof(pj_sockaddr_in); + addr_len = sizeof(local_addr); status = pj_sock_getsockname(sock, &local_addr, &addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); @@ -890,12 +921,13 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, } /* Initially set the address from the listener's address */ - local_addr.sin_addr.s_addr = - ((pj_sockaddr_in*)&listener->factory.local_addr)->sin_addr.s_addr; + if (!pj_sockaddr_has_addr(&local_addr)) { + pj_sockaddr_copy_addr(&local_addr, &listener->factory.local_addr); + } /* Create the transport descriptor */ status = tcp_create(listener, NULL, sock, PJ_FALSE, &local_addr, - (pj_sockaddr_in*)rem_addr, &tcp); + rem_addr, &tcp); if (status != PJ_SUCCESS) return status; @@ -903,7 +935,7 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, /* Start asynchronous connect() operation */ tcp->has_pending_connect = PJ_TRUE; status = pj_activesock_start_connect(tcp->asock, tcp->base.pool, rem_addr, - sizeof(pj_sockaddr_in)); + addr_len); if (status == PJ_SUCCESS) { on_connect_complete(tcp->asock, PJ_SUCCESS); } else if (status != PJ_EPENDING) { @@ -915,18 +947,17 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, /* Update (again) local address, just in case local address currently * set is different now that asynchronous connect() is started. */ - addr_len = sizeof(pj_sockaddr_in); + addr_len = sizeof(local_addr); if (pj_sock_getsockname(sock, &local_addr, &addr_len)==PJ_SUCCESS) { - pj_sockaddr_in *tp_addr = (pj_sockaddr_in*)&tcp->base.local_addr; + pj_sockaddr *tp_addr = &tcp->base.local_addr; /* Some systems (like old Win32 perhaps) may not set local address * properly before socket is fully connected. */ - if (tp_addr->sin_addr.s_addr != local_addr.sin_addr.s_addr && - local_addr.sin_addr.s_addr != 0) + if (pj_sockaddr_cmp(tp_addr, &local_addr) && + pj_sockaddr_get_port(&local_addr) != 0) { - tp_addr->sin_addr.s_addr = local_addr.sin_addr.s_addr; - tp_addr->sin_port = local_addr.sin_port; + pj_sockaddr_cp(tp_addr, &local_addr); sockaddr_to_host_port(tcp->base.pool, &tcp->base.local_name, &local_addr); } @@ -962,6 +993,7 @@ static pj_bool_t on_accept_complete(pj_activesock_t *asock, struct tcp_transport *tcp; char addr[PJ_INET6_ADDRSTRLEN+10]; pjsip_tp_state_callback state_cb; + pj_sockaddr tmp_src_addr; pj_status_t status; PJ_UNUSED_ARG(src_addr_len); @@ -985,13 +1017,19 @@ static pj_bool_t on_accept_complete(pj_activesock_t *asock, 2, listener->factory.obj_name, "incoming SIP TCP socket"); + /* tcp_create() expect pj_sockaddr, so copy src_addr to temporary var, + * just in case. + */ + pj_bzero(&tmp_src_addr, sizeof(tmp_src_addr)); + pj_sockaddr_cp(&tmp_src_addr, src_addr); + /* * Incoming connection! * Create TCP transport for the new socket. */ status = tcp_create( listener, NULL, sock, PJ_TRUE, - (const pj_sockaddr_in*)&listener->factory.local_addr, - (const pj_sockaddr_in*)src_addr, &tcp); + &listener->factory.local_addr, + &tmp_src_addr, &tcp); if (status == PJ_SUCCESS) { status = tcp_start_read(tcp); if (status != PJ_SUCCESS) { @@ -1095,9 +1133,9 @@ static pj_status_t tcp_send_msg(pjsip_transport *transport, PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX); /* Check the address is supported */ - PJ_ASSERT_RETURN(rem_addr && addr_len==sizeof(pj_sockaddr_in), PJ_EINVAL); - - + PJ_ASSERT_RETURN(rem_addr && (addr_len==sizeof(pj_sockaddr_in) || + addr_len==sizeof(pj_sockaddr_in6)), + PJ_EINVAL); /* Init op key. */ tdata->op_key.tdata = tdata; @@ -1122,10 +1160,19 @@ static pj_status_t tcp_send_msg(pjsip_transport *transport, /* * connect() is still in progress. Put the transmit data to * the delayed list. + * Starting from #1583 (https://trac.pjsip.org/repos/ticket/1583), + * we also add timeout value for the transmit data. When the + * connect() is completed, the timeout value will be checked to + * determine whether the transmit data needs to be sent. */ - delayed_tdata = PJ_POOL_ALLOC_T(tdata->pool, - struct delayed_tdata); + delayed_tdata = PJ_POOL_ZALLOC_T(tdata->pool, + struct delayed_tdata); delayed_tdata->tdata_op_key = &tdata->op_key; + if (tdata->msg && tdata->msg->type == PJSIP_REQUEST_MSG) { + pj_gettickcount(&delayed_tdata->timeout); + delayed_tdata->timeout.msec += pjsip_cfg()->tsx.td; + pj_time_val_normalize(&delayed_tdata->timeout); + } pj_list_push_back(&tcp->delayed_list, delayed_tdata); status = PJ_EPENDING; @@ -1269,7 +1316,7 @@ static pj_bool_t on_connect_complete(pj_activesock_t *asock, pj_status_t status) { struct tcp_transport *tcp; - pj_sockaddr_in addr; + pj_sockaddr addr; int addrlen; pjsip_tp_state_callback state_cb; @@ -1314,15 +1361,14 @@ static pj_bool_t on_connect_complete(pj_activesock_t *asock, * set is different now that the socket is connected (could happen * on some systems, like old Win32 probably?). */ - addrlen = sizeof(pj_sockaddr_in); + addrlen = sizeof(addr); if (pj_sock_getsockname(tcp->sock, &addr, &addrlen)==PJ_SUCCESS) { - pj_sockaddr_in *tp_addr = (pj_sockaddr_in*)&tcp->base.local_addr; + pj_sockaddr *tp_addr = &tcp->base.local_addr; if (pj_sockaddr_has_addr(&addr) && - tp_addr->sin_addr.s_addr != addr.sin_addr.s_addr) + pj_sockaddr_cmp(&addr, tp_addr) != 0) { - tp_addr->sin_addr.s_addr = addr.sin_addr.s_addr; - tp_addr->sin_port = addr.sin_port; + pj_sockaddr_cp(tp_addr, &addr); sockaddr_to_host_port(tcp->base.pool, &tcp->base.local_name, tp_addr); } diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c index 878b6db..2f244cc 100644 --- a/pjsip/src/pjsip/sip_transport_tls.c +++ b/pjsip/src/pjsip/sip_transport_tls.c @@ -1,4 +1,4 @@ -/* $Id: sip_transport_tls.c 4146 2012-05-30 06:35:59Z nanang $ */ +/* $Id: sip_transport_tls.c 4411 2013-03-04 04:34:38Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * @@ -55,6 +55,7 @@ struct tls_listener pjsip_endpoint *endpt; pjsip_tpmgr *tpmgr; pj_ssl_sock_t *ssock; + pj_sockaddr bound_addr; pj_ssl_cert_t *cert; pjsip_tls_setting tls_setting; }; @@ -71,6 +72,7 @@ struct delayed_tdata { PJ_DECL_LIST_MEMBER(struct delayed_tdata); pjsip_tx_data_op_key *tdata_op_key; + pj_time_val timeout; }; @@ -146,8 +148,8 @@ static pj_status_t tls_create(struct tls_listener *listener, pj_pool_t *pool, pj_ssl_sock_t *ssock, pj_bool_t is_server, - const pj_sockaddr_in *local, - const pj_sockaddr_in *remote, + const pj_sockaddr *local, + const pj_sockaddr *remote, const pj_str_t *remote_name, struct tls_transport **p_tls); @@ -165,10 +167,10 @@ static void tls_perror(const char *sender, const char *title, static void sockaddr_to_host_port( pj_pool_t *pool, pjsip_host_port *host_port, - const pj_sockaddr_in *addr ) + const pj_sockaddr *addr ) { host_port->host.ptr = (char*) pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+4); - pj_sockaddr_print(addr, host_port->host.ptr, PJ_INET6_ADDRSTRLEN+4, 2); + pj_sockaddr_print(addr, host_port->host.ptr, PJ_INET6_ADDRSTRLEN+4, 0); host_port->host.slen = pj_ansi_strlen(host_port->host.ptr); host_port->port = pj_sockaddr_get_port(addr); } @@ -234,29 +236,50 @@ static void tls_init_shutdown(struct tls_transport *tls, pj_status_t status) */ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt, const pjsip_tls_setting *opt, - const pj_sockaddr_in *local, + const pj_sockaddr_in *local_in, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory) +{ + pj_sockaddr local; + + if (local_in) + pj_sockaddr_cp(&local, local_in); + + return pjsip_tls_transport_start2(endpt, opt, (local_in? &local : NULL), + a_name, async_cnt, p_factory); +} + +PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt, + const pjsip_tls_setting *opt, + const pj_sockaddr *local, + const pjsip_host_port *a_name, + unsigned async_cnt, + pjsip_tpfactory **p_factory) { pj_pool_t *pool; + pj_bool_t is_ipv6; + int af; struct tls_listener *listener; pj_ssl_sock_param ssock_param; - pj_sockaddr_in *listener_addr; + pj_sockaddr *listener_addr; pj_bool_t has_listener; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL); + is_ipv6 = (local && local->addr.sa_family == pj_AF_INET6()); + af = is_ipv6 ? pj_AF_INET6() : pj_AF_INET(); + /* Verify that address given in a_name (if any) is valid */ if (a_name && a_name->host.slen) { - pj_sockaddr_in tmp; + pj_sockaddr tmp; - status = pj_sockaddr_in_init(&tmp, &a_name->host, - (pj_uint16_t)a_name->port); - if (status != PJ_SUCCESS || tmp.sin_addr.s_addr == PJ_INADDR_ANY || - tmp.sin_addr.s_addr == PJ_INADDR_NONE) + status = pj_sockaddr_init(af, &tmp, &a_name->host, + (pj_uint16_t)a_name->port); + if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) || + (!is_ipv6 && tmp.ipv4.sin_addr.s_addr == PJ_INADDR_NONE)) { /* Invalid address */ return PJ_EINVAL; @@ -269,19 +292,25 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt, listener = PJ_POOL_ZALLOC_T(pool, struct tls_listener); listener->factory.pool = pool; - listener->factory.type = PJSIP_TRANSPORT_TLS; - listener->factory.type_name = "tls"; + if (is_ipv6) + listener->factory.type = PJSIP_TRANSPORT_TLS6; + else + listener->factory.type = PJSIP_TRANSPORT_TLS; + listener->factory.type_name = (char*) + pjsip_transport_get_type_name(listener->factory.type); listener->factory.flag = - pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TLS); + pjsip_transport_get_flag_from_type(listener->factory.type); pj_ansi_strcpy(listener->factory.obj_name, "tlslis"); + if (is_ipv6) + pj_ansi_strcat(listener->factory.obj_name, "6"); if (opt) pjsip_tls_setting_copy(pool, &listener->tls_setting, opt); else pjsip_tls_setting_default(&listener->tls_setting); - status = pj_lock_create_recursive_mutex(pool, "tlslis", + status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name, &listener->factory.lock); if (status != PJ_SUCCESS) goto on_error; @@ -291,6 +320,7 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt, /* Build SSL socket param */ pj_ssl_sock_param_default(&ssock_param); + ssock_param.sock_af = af; ssock_param.cb.on_accept_complete = &on_accept_complete; ssock_param.cb.on_data_read = &on_data_read; ssock_param.cb.on_data_sent = &on_data_sent; @@ -337,12 +367,17 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt, if (status != PJ_SUCCESS) goto on_error; - listener_addr = (pj_sockaddr_in*)&listener->factory.local_addr; + /* Bind address may be different than factory.local_addr because + * factory.local_addr will be resolved below. + */ + listener_addr = &listener->factory.local_addr; if (local) { pj_sockaddr_cp((pj_sockaddr_t*)listener_addr, (const pj_sockaddr_t*)local); + pj_sockaddr_cp(&listener->bound_addr, local); } else { - pj_sockaddr_in_init(listener_addr, NULL, 0); + pj_sockaddr_init(af, listener_addr, NULL, 0); + pj_sockaddr_init(af, &listener->bound_addr, NULL, 0); } /* Check if certificate/CA list for SSL socket is set */ @@ -400,14 +435,14 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt, /* If the address returns 0.0.0.0, use the default * interface address as the transport's address. */ - if (listener_addr->sin_addr.s_addr == 0) { + if (!pj_sockaddr_has_addr(listener_addr)) { pj_sockaddr hostip; - status = pj_gethostip(pj_AF_INET(), &hostip); + status = pj_gethostip(af, &hostip); if (status != PJ_SUCCESS) goto on_error; - listener_addr->sin_addr.s_addr = hostip.ipv4.sin_addr.s_addr; + pj_sockaddr_copy_addr(listener_addr, &hostip); } /* Save the address name */ @@ -417,7 +452,7 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt, /* If port is zero, get the bound port */ if (listener->factory.addr_name.port == 0) { - listener->factory.addr_name.port = pj_ntohs(listener_addr->sin_port); + listener->factory.addr_name.port = pj_sockaddr_get_port(listener_addr); } pj_ansi_snprintf(listener->factory.obj_name, @@ -534,13 +569,14 @@ static pj_status_t tls_create( struct tls_listener *listener, pj_pool_t *pool, pj_ssl_sock_t *ssock, pj_bool_t is_server, - const pj_sockaddr_in *local, - const pj_sockaddr_in *remote, + const pj_sockaddr *local, + const pj_sockaddr *remote, const pj_str_t *remote_name, struct tls_transport **p_tls) { struct tls_transport *tls; const pj_str_t ka_pkt = PJSIP_TLS_KEEP_ALIVE_DATA; + char print_addr[PJ_INET6_ADDRSTRLEN+10]; pj_status_t status; @@ -578,17 +614,21 @@ static pj_status_t tls_create( struct tls_listener *listener, if (remote_name) pj_strdup(pool, &tls->remote_name, remote_name); - tls->base.key.type = PJSIP_TRANSPORT_TLS; - pj_memcpy(&tls->base.key.rem_addr, remote, sizeof(pj_sockaddr_in)); - tls->base.type_name = "tls"; - tls->base.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TLS); + tls->base.key.type = listener->factory.type; + pj_sockaddr_cp(&tls->base.key.rem_addr, remote); + tls->base.type_name = (char*)pjsip_transport_get_type_name( + (pjsip_transport_type_e)tls->base.key.type); + tls->base.flag = pjsip_transport_get_flag_from_type( + (pjsip_transport_type_e)tls->base.key.type); tls->base.info = (char*) pj_pool_alloc(pool, 64); - pj_ansi_snprintf(tls->base.info, 64, "TLS to %s:%d", - pj_inet_ntoa(remote->sin_addr), - (int)pj_ntohs(remote->sin_port)); + pj_ansi_snprintf(tls->base.info, 64, "%s to %s", + tls->base.type_name, + pj_sockaddr_print(remote, print_addr, + sizeof(print_addr), 3)); - tls->base.addr_len = sizeof(pj_sockaddr_in); + + tls->base.addr_len = pj_sockaddr_get_len(remote); tls->base.dir = is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING; /* Set initial local address */ @@ -599,11 +639,10 @@ static pj_status_t tls_create( struct tls_listener *listener, pj_sockaddr_cp(&tls->base.local_addr, local); } - sockaddr_to_host_port(pool, &tls->base.local_name, - (pj_sockaddr_in*)&tls->base.local_addr); + sockaddr_to_host_port(pool, &tls->base.local_name, &tls->base.local_addr); if (tls->remote_name.slen) { tls->base.remote_name.host = tls->remote_name; - tls->base.remote_name.port = pj_sockaddr_in_get_port(remote); + tls->base.remote_name.port = pj_sockaddr_get_port(remote); } else { sockaddr_to_host_port(pool, &tls->base.remote_name, remote); } @@ -647,6 +686,9 @@ on_error: /* Flush all delayed transmision once the socket is connected. */ static void tls_flush_pending_tx(struct tls_transport *tls) { + pj_time_val now; + + pj_gettickcount(&now); pj_lock_acquire(tls->base.lock); while (!pj_list_empty(&tls->delayed_list)) { struct delayed_tdata *pending_tx; @@ -661,13 +703,21 @@ static void tls_flush_pending_tx(struct tls_transport *tls) tdata = pending_tx->tdata_op_key->tdata; op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key; + if (pending_tx->timeout.sec > 0 && + PJ_TIME_VAL_GT(now, pending_tx->timeout)) + { + continue; + } + /* send! */ size = tdata->buf.cur - tdata->buf.start; status = pj_ssl_sock_send(tls->ssock, op_key, tdata->buf.start, &size, 0); if (status != PJ_EPENDING) { + pj_lock_release(tls->base.lock); on_data_sent(tls->ssock, op_key, size); + pj_lock_acquire(tls->base.lock); } } pj_lock_release(tls->base.lock); @@ -784,7 +834,7 @@ static pj_status_t tls_start_read(struct tls_transport *tls) { pj_pool_t *pool; pj_ssize_t size; - pj_sockaddr_in *rem_addr; + pj_sockaddr *rem_addr; void *readbuf[1]; pj_status_t status; @@ -807,11 +857,11 @@ static pj_status_t tls_start_read(struct tls_transport *tls) sizeof(pj_ioqueue_op_key_t)); tls->rdata.pkt_info.src_addr = tls->base.key.rem_addr; - tls->rdata.pkt_info.src_addr_len = sizeof(pj_sockaddr_in); - rem_addr = (pj_sockaddr_in*) &tls->base.key.rem_addr; - pj_ansi_strcpy(tls->rdata.pkt_info.src_name, - pj_inet_ntoa(rem_addr->sin_addr)); - tls->rdata.pkt_info.src_port = pj_ntohs(rem_addr->sin_port); + tls->rdata.pkt_info.src_addr_len = sizeof(tls->rdata.pkt_info.src_addr); + rem_addr = &tls->base.key.rem_addr; + pj_sockaddr_print(rem_addr, tls->rdata.pkt_info.src_name, + sizeof(tls->rdata.pkt_info.src_name), 0); + tls->rdata.pkt_info.src_port = pj_sockaddr_get_port(rem_addr); size = sizeof(tls->rdata.pkt_info.packet); readbuf[0] = tls->rdata.pkt_info.packet; @@ -844,7 +894,7 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, pj_pool_t *pool; pj_ssl_sock_t *ssock; pj_ssl_sock_param ssock_param; - pj_sockaddr_in local_addr; + pj_sockaddr local_addr; pj_str_t remote_name; pj_status_t status; @@ -852,9 +902,11 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr && addr_len && p_transport, PJ_EINVAL); - /* Check that address is a sockaddr_in */ - PJ_ASSERT_RETURN(rem_addr->addr.sa_family == pj_AF_INET() && - addr_len == sizeof(pj_sockaddr_in), PJ_EINVAL); + /* Check that address is a sockaddr_in or sockaddr_in6*/ + PJ_ASSERT_RETURN((rem_addr->addr.sa_family == pj_AF_INET() && + addr_len == sizeof(pj_sockaddr_in)) || + (rem_addr->addr.sa_family == pj_AF_INET6() && + addr_len == sizeof(pj_sockaddr_in6)), PJ_EINVAL); listener = (struct tls_listener*)factory; @@ -871,6 +923,8 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, /* Build SSL socket param */ pj_ssl_sock_param_default(&ssock_param); + ssock_param.sock_af = (factory->type & PJSIP_TRANSPORT_IPV6) ? + pj_AF_INET6() : pj_AF_INET(); ssock_param.cb.on_connect_complete = &on_connect_complete; ssock_param.cb.on_data_read = &on_data_read; ssock_param.cb.on_data_sent = &on_data_sent; @@ -921,12 +975,14 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, return status; } - /* Initially set bind address to PJ_INADDR_ANY port 0 */ - pj_sockaddr_in_init(&local_addr, NULL, 0); + /* Initially set bind address to listener's bind address */ + pj_sockaddr_init(listener->bound_addr.addr.sa_family, + &local_addr, NULL, 0); + pj_sockaddr_copy_addr(&local_addr, &listener->bound_addr); /* Create the transport descriptor */ status = tls_create(listener, pool, ssock, PJ_FALSE, &local_addr, - (pj_sockaddr_in*)rem_addr, &remote_name, &tls); + rem_addr, &remote_name, &tls); if (status != PJ_SUCCESS) return status; @@ -973,7 +1029,7 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, } sockaddr_to_host_port(tls->base.pool, &tls->base.local_name, - (pj_sockaddr_in*)&tls->base.local_addr); + &tls->base.local_addr); } PJ_LOG(4,(tls->base.obj_name, @@ -1007,6 +1063,7 @@ static pj_bool_t on_accept_complete(pj_ssl_sock_t *ssock, pj_ssl_sock_info ssl_info; char addr[PJ_INET6_ADDRSTRLEN+10]; pjsip_tp_state_callback state_cb; + pj_sockaddr tmp_src_addr; pj_bool_t is_shutdown; pj_status_t status; @@ -1034,13 +1091,17 @@ static pj_bool_t on_accept_complete(pj_ssl_sock_t *ssock, return PJ_TRUE; } + /* Copy to larger buffer, just in case */ + pj_bzero(&tmp_src_addr, sizeof(tmp_src_addr)); + pj_sockaddr_cp(&tmp_src_addr, src_addr); + /* * Incoming connection! * Create TLS transport for the new socket. */ status = tls_create( listener, NULL, new_ssock, PJ_TRUE, - (const pj_sockaddr_in*)&listener->factory.local_addr, - (const pj_sockaddr_in*)src_addr, NULL, &tls); + &listener->factory.local_addr, + &tmp_src_addr, NULL, &tls); if (status != PJ_SUCCESS) return PJ_TRUE; @@ -1190,9 +1251,9 @@ static pj_status_t tls_send_msg(pjsip_transport *transport, PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX); /* Check the address is supported */ - PJ_ASSERT_RETURN(rem_addr && addr_len==sizeof(pj_sockaddr_in), PJ_EINVAL); - - + PJ_ASSERT_RETURN(rem_addr && (addr_len==sizeof(pj_sockaddr_in) || + addr_len==sizeof(pj_sockaddr_in6)), + PJ_EINVAL); /* Init op key. */ tdata->op_key.tdata = tdata; @@ -1217,10 +1278,19 @@ static pj_status_t tls_send_msg(pjsip_transport *transport, /* * connect() is still in progress. Put the transmit data to * the delayed list. + * Starting from #1583 (https://trac.pjsip.org/repos/ticket/1583), + * we also add timeout value for the transmit data. When the + * connect() is completed, the timeout value will be checked to + * determine whether the transmit data needs to be sent. */ - delayed_tdata = PJ_POOL_ALLOC_T(tdata->pool, - struct delayed_tdata); + delayed_tdata = PJ_POOL_ZALLOC_T(tdata->pool, + struct delayed_tdata); delayed_tdata->tdata_op_key = &tdata->op_key; + if (tdata->msg && tdata->msg->type == PJSIP_REQUEST_MSG) { + pj_gettickcount(&delayed_tdata->timeout); + delayed_tdata->timeout.msec += pjsip_cfg()->tsx.td; + pj_time_val_normalize(&delayed_tdata->timeout); + } pj_list_push_back(&tls->delayed_list, delayed_tdata); status = PJ_EPENDING; @@ -1365,7 +1435,7 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock, { struct tls_transport *tls; pj_ssl_sock_info ssl_info; - pj_sockaddr_in addr, *tp_addr; + pj_sockaddr addr, *tp_addr; pjsip_tp_state_callback state_cb; pj_bool_t is_shutdown; @@ -1403,12 +1473,11 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock, * set is different now that the socket is connected (could happen * on some systems, like old Win32 probably?). */ - tp_addr = (pj_sockaddr_in*)&tls->base.local_addr; + tp_addr = &tls->base.local_addr; pj_sockaddr_cp((pj_sockaddr_t*)&addr, (pj_sockaddr_t*)&ssl_info.local_addr); - if (tp_addr->sin_addr.s_addr != addr.sin_addr.s_addr) { - tp_addr->sin_addr.s_addr = addr.sin_addr.s_addr; - tp_addr->sin_port = addr.sin_port; + if (pj_sockaddr_cmp(tp_addr, &addr) != 0) { + pj_sockaddr_cp(tp_addr, &addr); sockaddr_to_host_port(tls->base.pool, &tls->base.local_name, tp_addr); } diff --git a/pjsip/src/pjsip/sip_ua_layer.c b/pjsip/src/pjsip/sip_ua_layer.c index 7128891..723c061 100644 --- a/pjsip/src/pjsip/sip_ua_layer.c +++ b/pjsip/src/pjsip/sip_ua_layer.c @@ -1,4 +1,4 @@ -/* $Id: sip_ua_layer.c 3664 2011-07-19 03:42:28Z nanang $ */ +/* $Id: sip_ua_layer.c 4208 2012-07-18 07:52:33Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -302,9 +302,10 @@ PJ_DEF(pj_status_t) pjsip_ua_register_dlg( pjsip_user_agent *ua, struct dlg_set *dlg_set; dlg_set = (struct dlg_set*) - pj_hash_get( mod_ua.dlg_table, dlg->local.info->tag.ptr, - dlg->local.info->tag.slen, - &dlg->local.tag_hval); + pj_hash_get_lower( mod_ua.dlg_table, + dlg->local.info->tag.ptr, + dlg->local.info->tag.slen, + &dlg->local.tag_hval); if (dlg_set) { /* This is NOT the first dialog in the dialog set. @@ -326,9 +327,11 @@ PJ_DEF(pj_status_t) pjsip_ua_register_dlg( pjsip_user_agent *ua, dlg->dlg_set = dlg_set; /* Register the dialog set in the hash table. */ - pj_hash_set_np(mod_ua.dlg_table, - dlg->local.info->tag.ptr, dlg->local.info->tag.slen, - dlg->local.tag_hval, dlg_set->ht_entry, dlg_set); + pj_hash_set_np_lower(mod_ua.dlg_table, + dlg->local.info->tag.ptr, + dlg->local.info->tag.slen, + dlg->local.tag_hval, dlg_set->ht_entry, + dlg_set); } } else { @@ -341,9 +344,10 @@ PJ_DEF(pj_status_t) pjsip_ua_register_dlg( pjsip_user_agent *ua, dlg->dlg_set = dlg_set; - pj_hash_set_np(mod_ua.dlg_table, - dlg->local.info->tag.ptr, dlg->local.info->tag.slen, - dlg->local.tag_hval, dlg_set->ht_entry, dlg_set); + pj_hash_set_np_lower(mod_ua.dlg_table, + dlg->local.info->tag.ptr, + dlg->local.info->tag.slen, + dlg->local.tag_hval, dlg_set->ht_entry, dlg_set); } /* Unlock user agent. */ @@ -387,8 +391,9 @@ PJ_DEF(pj_status_t) pjsip_ua_unregister_dlg( pjsip_user_agent *ua, /* If dialog list is empty, remove the dialog set from the hash table. */ if (pj_list_empty(&dlg_set->dlg_list)) { - pj_hash_set(NULL, mod_ua.dlg_table, dlg->local.info->tag.ptr, - dlg->local.info->tag.slen, dlg->local.tag_hval, NULL); + pj_hash_set_lower(NULL, mod_ua.dlg_table, dlg->local.info->tag.ptr, + dlg->local.info->tag.slen, dlg->local.tag_hval, + NULL); /* Return dlg_set to free nodes. */ pj_list_push_back(&mod_ua.free_dlgset_nodes, dlg_set); @@ -449,8 +454,8 @@ PJ_DEF(pjsip_dialog*) pjsip_ua_find_dialog(const pj_str_t *call_id, /* Lookup the dialog set. */ dlg_set = (struct dlg_set*) - pj_hash_get(mod_ua.dlg_table, local_tag->ptr, local_tag->slen, - NULL); + pj_hash_get_lower(mod_ua.dlg_table, local_tag->ptr, + local_tag->slen, NULL); if (dlg_set == NULL) { /* Not found */ pj_mutex_unlock(mod_ua.mutex); @@ -462,7 +467,7 @@ PJ_DEF(pjsip_dialog*) pjsip_ua_find_dialog(const pj_str_t *call_id, */ dlg = dlg_set->dlg_list.next; while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) { - if (pj_strcmp(&dlg->remote.info->tag, remote_tag) == 0) + if (pj_stricmp(&dlg->remote.info->tag, remote_tag) == 0) break; dlg = dlg->next; } @@ -563,7 +568,8 @@ static struct dlg_set *find_dlg_set_for_msg( pjsip_rx_data *rdata ) /* Lookup the dialog set. */ dlg_set = (struct dlg_set*) - pj_hash_get(mod_ua.dlg_table, tag->ptr, tag->slen, NULL); + pj_hash_get_lower(mod_ua.dlg_table, tag->ptr, tag->slen, + NULL); return dlg_set; } } @@ -624,7 +630,7 @@ retry_on_deadlock: dlg = dlg_set->dlg_list.next; while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) { - if (pj_strcmp(&dlg->remote.info->tag, from_tag) == 0) + if (pj_stricmp(&dlg->remote.info->tag, from_tag) == 0) break; dlg = dlg->next; @@ -761,10 +767,10 @@ retry_on_deadlock: /* Get the dialog set. */ dlg_set = (struct dlg_set*) - pj_hash_get(mod_ua.dlg_table, - rdata->msg_info.from->tag.ptr, - rdata->msg_info.from->tag.slen, - NULL); + pj_hash_get_lower(mod_ua.dlg_table, + rdata->msg_info.from->tag.ptr, + rdata->msg_info.from->tag.slen, + NULL); if (!dlg_set) { /* Unlock dialog hash table. */ @@ -812,7 +818,7 @@ retry_on_deadlock: break; /* Otherwise find the one with matching To tag. */ - if (pj_strcmp(to_tag, &dlg->remote.info->tag) == 0) + if (pj_stricmp(to_tag, &dlg->remote.info->tag) == 0) break; dlg = dlg->next; diff --git a/pjsip/src/pjsip/sip_uri.c b/pjsip/src/pjsip/sip_uri.c index fba163c..4d54e2e 100644 --- a/pjsip/src/pjsip/sip_uri.c +++ b/pjsip/src/pjsip/sip_uri.c @@ -1,4 +1,4 @@ -/* $Id: sip_uri.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* $Id: sip_uri.c 4228 2012-08-13 07:26:03Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -269,7 +269,10 @@ static pj_ssize_t pjsip_url_print( pjsip_uri_context_e context, /* Print "user:password@", if any. */ if (url->user.slen) { - copy_advance_escape(buf, url->user, pc->pjsip_USER_SPEC); + const pj_cis_t *spec = pjsip_cfg()->endpt.allow_tx_hash_in_uri ? + &pc->pjsip_USER_SPEC_LENIENT : + &pc->pjsip_USER_SPEC; + copy_advance_escape(buf, url->user, *spec); if (url->passwd.slen) { *buf++ = ':'; copy_advance_escape(buf, url->passwd, pc->pjsip_PASSWD_SPEC); diff --git a/pjsip/src/pjsip/sip_util_proxy.c b/pjsip/src/pjsip/sip_util_proxy.c index 7a34534..02069b9 100644 --- a/pjsip/src/pjsip/sip_util_proxy.c +++ b/pjsip/src/pjsip/sip_util_proxy.c @@ -1,4 +1,4 @@ -/* $Id: sip_util_proxy.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* $Id: sip_util_proxy.c 4208 2012-07-18 07:52:33Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -345,7 +345,7 @@ PJ_DEF(pj_str_t) pjsip_calculate_branch_id( pjsip_rx_data *rdata ) /* If incoming request does not have RFC 3261 branch value, create * a branch value from GUID . */ - if (pj_strncmp(&rdata->msg_info.via->branch_param, + if (pj_strnicmp(&rdata->msg_info.via->branch_param, &rfc3261_branch, PJSIP_RFC3261_BRANCH_LEN) != 0 ) { pj_str_t tmp; diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index c5278c4..5d7695d 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -1,4 +1,4 @@ -/* $Id: pjsua_acc.c 4185 2012-06-28 14:16:05Z ming $ */ +/* $Id: pjsua_acc.c 4318 2013-01-16 09:51:45Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -133,6 +133,9 @@ PJ_DEF(void) pjsua_acc_config_dup( pj_pool_t *pool, pjsua_transport_config_dup(pool, &dst->rtp_cfg, &src->rtp_cfg); + pjsua_ice_config_dup(pool, &dst->ice_cfg, &src->ice_cfg); + pjsua_turn_config_dup(pool, &dst->turn_cfg, &src->turn_cfg); + pj_strdup(pool, &dst->ka_data, &src->ka_data); } @@ -283,7 +286,7 @@ static pj_status_t initialize_acc(unsigned acc_id) * contact params. */ #if PJSUA_ADD_ICE_TAGS - if (pjsua_var.media_cfg.enable_ice) { + if (acc_cfg->ice_cfg.enable_ice) { unsigned new_len; pj_str_t new_prm; @@ -348,6 +351,18 @@ static pj_status_t initialize_acc(unsigned acc_id) } } + /* If account's ICE and TURN customization is not set, then + * initialize it with the settings from the global media config. + */ + if (acc->cfg.ice_cfg_use == PJSUA_ICE_CONFIG_USE_DEFAULT) { + pjsua_ice_config_from_media_config(NULL, &acc->cfg.ice_cfg, + &pjsua_var.media_cfg); + } + if (acc->cfg.turn_cfg_use == PJSUA_TURN_CONFIG_USE_DEFAULT) { + pjsua_turn_config_from_media_config(NULL, &acc->cfg.turn_cfg, + &pjsua_var.media_cfg); + } + /* Mark account as valid */ pjsua_var.acc[acc_id].valid = PJ_TRUE; @@ -470,6 +485,10 @@ PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg, /* Otherwise subscribe to MWI, if it's enabled */ if (pjsua_var.acc[id].cfg.mwi_enabled) pjsua_start_mwi(id, PJ_TRUE); + + /* Start publish too */ + if (acc->cfg.publish_enabled) + pjsua_pres_init_publish_acc(id); } pj_log_pop_indent(); @@ -1152,6 +1171,70 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id, update_reg = PJ_TRUE; } + /* Video settings */ + acc->cfg.vid_in_auto_show = cfg->vid_in_auto_show; + acc->cfg.vid_out_auto_transmit = cfg->vid_out_auto_transmit; + acc->cfg.vid_wnd_flags = cfg->vid_wnd_flags; + acc->cfg.vid_cap_dev = cfg->vid_cap_dev; + acc->cfg.vid_rend_dev = cfg->vid_rend_dev; + acc->cfg.vid_stream_rc_cfg = cfg->vid_stream_rc_cfg; + + /* Media settings */ + if (pj_stricmp(&acc->cfg.rtp_cfg.public_addr, &cfg->rtp_cfg.public_addr) || + pj_stricmp(&acc->cfg.rtp_cfg.bound_addr, &cfg->rtp_cfg.bound_addr)) + { + pjsua_transport_config_dup(acc->pool, &acc->cfg.rtp_cfg, + &cfg->rtp_cfg); + } else { + /* ..to save memory by not using the pool */ + acc->cfg.rtp_cfg = cfg->rtp_cfg; + } + + acc->cfg.ipv6_media_use = cfg->ipv6_media_use; + + /* STUN and Media customization */ + if (acc->cfg.sip_stun_use != cfg->sip_stun_use) { + acc->cfg.sip_stun_use = cfg->sip_stun_use; + update_reg = PJ_TRUE; + } + acc->cfg.media_stun_use = cfg->media_stun_use; + + /* ICE settings */ + acc->cfg.ice_cfg_use = cfg->ice_cfg_use; + switch (acc->cfg.ice_cfg_use) { + case PJSUA_ICE_CONFIG_USE_DEFAULT: + /* Copy ICE settings from media settings so that we don't need to + * check the media config if we look for ICE config. + */ + pjsua_ice_config_from_media_config(NULL, &acc->cfg.ice_cfg, + &pjsua_var.media_cfg); + break; + case PJSUA_ICE_CONFIG_USE_CUSTOM: + pjsua_ice_config_dup(acc->pool, &acc->cfg.ice_cfg, &cfg->ice_cfg); + break; + } + + /* TURN settings */ + acc->cfg.turn_cfg_use = cfg->turn_cfg_use; + switch (acc->cfg.turn_cfg_use) { + case PJSUA_TURN_CONFIG_USE_DEFAULT: + /* Copy TURN settings from media settings so that we don't need to + * check the media config if we look for TURN config. + */ + pjsua_turn_config_from_media_config(NULL, &acc->cfg.turn_cfg, + &pjsua_var.media_cfg); + break; + case PJSUA_TURN_CONFIG_USE_CUSTOM: + pjsua_turn_config_dup(acc->pool, &acc->cfg.turn_cfg, + &cfg->turn_cfg); + break; + } + + acc->cfg.use_srtp = cfg->use_srtp; + + /* Call hold type */ + acc->cfg.call_hold_type = cfg->call_hold_type; + /* Unregister first */ if (unreg_first) { pjsua_acc_set_registration(acc->index, PJ_FALSE); @@ -1174,16 +1257,6 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id, pjsua_start_mwi(acc_id, PJ_TRUE); } - /* Video settings */ - acc->cfg.vid_in_auto_show = cfg->vid_in_auto_show; - acc->cfg.vid_out_auto_transmit = cfg->vid_out_auto_transmit; - acc->cfg.vid_wnd_flags = cfg->vid_wnd_flags; - acc->cfg.vid_cap_dev = cfg->vid_cap_dev; - acc->cfg.vid_rend_dev = cfg->vid_rend_dev; - - /* Call hold type */ - acc->cfg.call_hold_type = cfg->call_hold_type; - on_return: PJSUA_UNLOCK(); pj_log_pop_indent(); @@ -1515,6 +1588,7 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc, const char *ob = ";ob"; char *tmp; const char *beginquote, *endquote; + char transport_param[32]; int len; /* Enclose IPv6 address in square brackets */ @@ -1525,9 +1599,21 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc, beginquote = endquote = ""; } + /* Don't add transport parameter if it's UDP */ + if (tp->key.type != PJSIP_TRANSPORT_UDP && + tp->key.type != PJSIP_TRANSPORT_UDP6) + { + pj_ansi_snprintf(transport_param, sizeof(transport_param), + ";transport=%s", + pjsip_transport_get_type_name( + (pjsip_transport_type_e)tp->key.type)); + } else { + transport_param[0] = '\0'; + } + tmp = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); len = pj_ansi_snprintf(tmp, PJSIP_MAX_URL_SIZE, - "%.*s", + "%.*s", (int)acc->user_part.slen, acc->user_part.ptr, (acc->user_part.slen? "@" : ""), @@ -1536,7 +1622,7 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc, via_addr->ptr, endquote, rport, - tp->type_name, + transport_param, (int)acc->cfg.contact_uri_params.slen, acc->cfg.contact_uri_params.ptr, (acc->cfg.use_rfc5626? ob: ""), @@ -1776,9 +1862,26 @@ static void update_keep_alive(pjsua_acc *acc, pj_bool_t start, /* Save transport and destination address. */ acc->ka_transport = param->rdata->tp_info.transport; pjsip_transport_add_ref(acc->ka_transport); - pj_memcpy(&acc->ka_target, ¶m->rdata->pkt_info.src_addr, - param->rdata->pkt_info.src_addr_len); - acc->ka_target_len = param->rdata->pkt_info.src_addr_len; + + /* https://trac.pjsip.org/repos/ticket/1607: + * Calculate the destination address from the original request. Some + * (broken) servers send the response using different source address + * than the one that receives the request, which is forbidden by RFC + * 3581. + */ + { + pjsip_transaction *tsx; + pjsip_tx_data *req; + + tsx = pjsip_rdata_get_tsx(param->rdata); + PJ_ASSERT_ON_FAIL(tsx, return); + + req = tsx->last_tx; + + pj_memcpy(&acc->ka_target, &req->tp_info.dst_addr, + req->tp_info.dst_addr_len); + acc->ka_target_len = req->tp_info.dst_addr_len; + } /* Setup and start the timer */ acc->ka_timer.cb = &keep_alive_timer_cb; @@ -2146,6 +2249,13 @@ static pj_status_t pjsua_regc_init(int acc_id) return PJ_SUCCESS; } +pj_bool_t pjsua_sip_acc_is_using_stun(pjsua_acc_id acc_id) +{ + pjsua_acc *acc = &pjsua_var.acc[acc_id]; + + return acc->cfg.sip_stun_use != PJSUA_STUN_USE_DISABLED && + pjsua_var.ua_cfg.stun_srv_cnt != 0; +} /* * Update registration or perform unregistration. @@ -2153,6 +2263,7 @@ static pj_status_t pjsua_regc_init(int acc_id) PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id, pj_bool_t renew) { + pjsua_acc *acc; pj_status_t status = 0; pjsip_tx_data *tdata = 0; @@ -2166,6 +2277,8 @@ PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id, PJSUA_LOCK(); + acc = &pjsua_var.acc[acc_id]; + /* Cancel any re-registration timer */ if (pjsua_var.acc[acc_id].auto_rereg.timer.id) { pjsua_var.acc[acc_id].auto_rereg.timer.id = PJ_FALSE; @@ -2232,6 +2345,15 @@ PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id, pjsip_regc_set_via_sent_by(pjsua_var.acc[acc_id].regc, &pjsua_var.acc[acc_id].via_addr, pjsua_var.acc[acc_id].via_tp); + } else if (!pjsua_sip_acc_is_using_stun(acc_id)) { + /* Choose local interface to use in Via if acc is not using + * STUN + */ + pjsua_acc_get_uac_addr(acc_id, tdata->pool, + &acc->cfg.reg_uri, + &tdata->via_addr, + NULL, NULL, + &tdata->via_tp); } //pjsua_process_msg_data(tdata, NULL); @@ -2605,6 +2727,15 @@ PJ_DEF(pj_status_t) pjsua_acc_create_request(pjsua_acc_id acc_id, { tdata->via_addr = pjsua_var.acc[acc_id].via_addr; tdata->via_tp = pjsua_var.acc[acc_id].via_tp; + } else if (!pjsua_sip_acc_is_using_stun(acc_id)) { + /* Choose local interface to use in Via if acc is not using + * STUN + */ + pjsua_acc_get_uac_addr(acc_id, tdata->pool, + target, + &tdata->via_addr, + NULL, NULL, + &tdata->via_tp); } /* Done */ @@ -2612,46 +2743,40 @@ PJ_DEF(pj_status_t) pjsua_acc_create_request(pjsua_acc_id acc_id, return PJ_SUCCESS; } - -PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool, - pj_str_t *contact, - pjsua_acc_id acc_id, - const pj_str_t *suri) +/* Get local transport address suitable to be used for Via or Contact address + * to send request to the specified destination URI. + */ +pj_status_t pjsua_acc_get_uac_addr(pjsua_acc_id acc_id, + pj_pool_t *pool, + const pj_str_t *dst_uri, + pjsip_host_port *addr, + pjsip_transport_type_e *p_tp_type, + int *secure, + const void **p_tp) { pjsua_acc *acc; pjsip_sip_uri *sip_uri; pj_status_t status; pjsip_transport_type_e tp_type = PJSIP_TRANSPORT_UNSPECIFIED; - pj_str_t local_addr; - pjsip_tpselector tp_sel; unsigned flag; - int secure; - int local_port; - const char *beginquote, *endquote; - char transport_param[32]; - const char *ob = ";ob"; + pjsip_tpselector tp_sel; + pjsip_tpmgr *tpmgr; + pjsip_tpmgr_fla2_param tfla2_prm; - PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL); acc = &pjsua_var.acc[acc_id]; - /* If force_contact is configured, then use use it */ - if (acc->cfg.force_contact.slen) { - *contact = acc->cfg.force_contact; - return PJ_SUCCESS; - } - - /* If route-set is configured for the account, then URI is the + /* If route-set is configured for the account, then URI is the * first entry of the route-set. */ if (!pj_list_empty(&acc->route_set)) { - sip_uri = (pjsip_sip_uri*) + sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(acc->route_set.next->name_addr.uri); } else { pj_str_t tmp; pjsip_uri *uri; - pj_strdup_with_null(pool, &tmp, suri); + pj_strdup_with_null(pool, &tmp, dst_uri); uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0); if (uri == NULL) @@ -2671,7 +2796,7 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool, tp_type = PJSIP_TRANSPORT_UDP; } else tp_type = pjsip_transport_get_type_from_name(&sip_uri->transport_param); - + if (tp_type == PJSIP_TRANSPORT_UNSPECIFIED) return PJSIP_EUNSUPTRANSPORT; @@ -2682,15 +2807,66 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool, tp_type = (pjsip_transport_type_e)(((int)tp_type) + PJSIP_TRANSPORT_IPV6); flag = pjsip_transport_get_flag_from_type(tp_type); - secure = (flag & PJSIP_TRANSPORT_SECURE) != 0; /* Init transport selector. */ - pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel); + pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel); /* Get local address suitable to send request from */ - status = pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(pjsua_var.endpt), - pool, tp_type, &tp_sel, - &local_addr, &local_port); + pjsip_tpmgr_fla2_param_default(&tfla2_prm); + tfla2_prm.tp_type = tp_type; + tfla2_prm.tp_sel = &tp_sel; + tfla2_prm.dst_host = sip_uri->host; + tfla2_prm.local_if = (!pjsua_sip_acc_is_using_stun(acc_id) || + (flag & PJSIP_TRANSPORT_RELIABLE)); + + tpmgr = pjsip_endpt_get_tpmgr(pjsua_var.endpt); + status = pjsip_tpmgr_find_local_addr2(tpmgr, pool, &tfla2_prm); + if (status != PJ_SUCCESS) + return status; + + addr->host = tfla2_prm.ret_addr; + addr->port = tfla2_prm.ret_port; + + if (p_tp_type) + *p_tp_type = tp_type; + + if (secure) { + *secure = (flag & PJSIP_TRANSPORT_SECURE) != 0; + } + + if (p_tp) + *p_tp = tfla2_prm.ret_tp; + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool, + pj_str_t *contact, + pjsua_acc_id acc_id, + const pj_str_t *suri) +{ + pjsua_acc *acc; + pj_status_t status; + pjsip_transport_type_e tp_type; + pjsip_host_port addr; + int secure; + const char *beginquote, *endquote; + char transport_param[32]; + const char *ob = ";ob"; + + + PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL); + acc = &pjsua_var.acc[acc_id]; + + /* If force_contact is configured, then use use it */ + if (acc->cfg.force_contact.slen) { + *contact = acc->cfg.force_contact; + return PJ_SUCCESS; + } + + status = pjsua_acc_get_uac_addr(acc_id, pool, suri, &addr, + &tp_type, &secure, NULL); if (status != PJ_SUCCESS) return status; @@ -2725,10 +2901,10 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool, acc->user_part.ptr, (acc->user_part.slen?"@":""), beginquote, - (int)local_addr.slen, - local_addr.ptr, + (int)addr.host.slen, + addr.host.ptr, endquote, - local_port, + addr.port, transport_param, (int)acc->cfg.contact_uri_params.slen, acc->cfg.contact_uri_params.ptr, @@ -2760,6 +2936,8 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uas_contact( pj_pool_t *pool, pjsip_transport_type_e tp_type = PJSIP_TRANSPORT_UNSPECIFIED; pj_str_t local_addr; pjsip_tpselector tp_sel; + pjsip_tpmgr *tpmgr; + pjsip_tpmgr_fla2_param tfla2_prm; unsigned flag; int secure; int local_port; @@ -2847,12 +3025,22 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uas_contact( pj_pool_t *pool, pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel); /* Get local address suitable to send request from */ - status = pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(pjsua_var.endpt), - pool, tp_type, &tp_sel, - &local_addr, &local_port); + pjsip_tpmgr_fla2_param_default(&tfla2_prm); + tfla2_prm.tp_type = tp_type; + tfla2_prm.tp_sel = &tp_sel; + tfla2_prm.dst_host = sip_uri->host; + tfla2_prm.local_if = (!pjsua_sip_acc_is_using_stun(acc_id) || + (flag & PJSIP_TRANSPORT_RELIABLE)); + + tpmgr = pjsip_endpt_get_tpmgr(pjsua_var.endpt); + status = pjsip_tpmgr_find_local_addr2(tpmgr, pool, &tfla2_prm); if (status != PJ_SUCCESS) return status; + local_addr = tfla2_prm.ret_addr; + local_port = tfla2_prm.ret_port; + + /* Enclose IPv6 address in square brackets */ if (tp_type & PJSIP_TRANSPORT_IPV6) { beginquote = "["; diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index 557a147..1163e08 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -1,4 +1,4 @@ -/* $Id: pjsua_aud.c 4145 2012-05-22 23:13:22Z bennylp $ */ +/* $Id: pjsua_aud.c 4336 2013-01-29 08:15:02Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -291,7 +291,8 @@ pj_status_t pjsua_aud_subsys_init() /* Init the passthrough codec with supported formats only */ codec_cfg.passthrough.setting.fmt_cnt = ext_fmt_cnt; codec_cfg.passthrough.setting.fmts = ext_fmts; - codec_cfg.passthrough.setting.ilbc_mode = cfg->ilbc_mode; + codec_cfg.passthrough.setting.ilbc_mode = + pjsua_var.media_cfg.ilbc_mode; } #endif /* PJMEDIA_HAS_PASSTHROUGH_CODECS */ @@ -559,7 +560,12 @@ static void dtmf_callback(pjmedia_stream *strm, void *user_data, pj_log_pop_indent(); } - +/* Internal function: update audio channel after SDP negotiation. + * Warning: do not use temporary/flip-flop pool, e.g: inv->pool_prov, + * for creating stream, etc, as after SDP negotiation and when + * the SDP media is not changed, the stream should remain running + * while the temporary/flip-flop pool may be released. + */ pj_status_t pjsua_aud_channel_update(pjsua_call_media *call_med, pj_pool_t *tmp_pool, pjmedia_stream_info *si, @@ -583,20 +589,6 @@ pj_status_t pjsua_aud_channel_update(pjsua_call_media *call_med, /* Check if no media is active */ if (si->dir != PJMEDIA_DIR_NONE) { - /* Override ptime, if this option is specified. */ - if (pjsua_var.media_cfg.ptime != 0) { - si->param->setting.frm_per_pkt = (pj_uint8_t) - (pjsua_var.media_cfg.ptime / si->param->info.frm_ptime); - if (si->param->setting.frm_per_pkt == 0) - si->param->setting.frm_per_pkt = 1; - } - - /* Disable VAD, if this option is specified. */ - if (pjsua_var.media_cfg.no_vad) { - si->param->setting.vad = 0; - } - - /* Optionally, application may modify other stream settings here * (such as jitter buffer parameters, codec ptime, etc.) */ @@ -678,7 +670,7 @@ pj_status_t pjsua_aud_channel_update(pjsua_call_media *call_med, port_name = pj_str("call"); } status = pjmedia_conf_add_port( pjsua_var.mconf, - call->inv->pool_prov, + call->inv->pool, media_port, &port_name, (unsigned*) @@ -1571,6 +1563,14 @@ static pj_status_t create_aud_param(pjmedia_aud_param *param, param->flags &= ~(PJMEDIA_AUD_DEV_CAP_EC|PJMEDIA_AUD_DEV_CAP_EC_TAIL); } + /* VAD settings */ + if (pjsua_var.media_cfg.no_vad) { + param->flags &= ~PJMEDIA_AUD_DEV_CAP_VAD; + } else { + param->flags |= PJMEDIA_AUD_DEV_CAP_VAD; + param->vad_enabled = PJ_TRUE; + } + return PJ_SUCCESS; } diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 6f14709..7329333 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -1,4 +1,4 @@ -/* $Id: pjsua_call.c 4176 2012-06-23 03:06:52Z nanang $ */ +/* $Id: pjsua_call.c 4411 2013-03-04 04:34:38Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -106,6 +106,12 @@ static pj_status_t create_sdp_of_call_hold(pjsua_call *call, static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event); static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event); +/* Timer callback to send re-INVITE/UPDATE to lock codec or ICE update */ +static void reinv_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry); + +/* Check and send reinvite for lock codec and ICE update */ +static pj_status_t process_pending_reinvite(pjsua_call *call); + /* * Reset call descriptor. */ @@ -128,6 +134,8 @@ static void reset_call(pjsua_call_id id) call_med->tp_auto_del = PJ_TRUE; } pjsua_call_setting_default(&call->opt); + pj_timer_entry_init(&call->reinv_timer, PJ_FALSE, + (void*)(pj_size_t)id, &reinv_timer_cb); } @@ -179,6 +187,10 @@ pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg) pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED, NULL, 1, &str_norefersub); + /* Add "INFO" in Allow header, for DTMF and video key frame request. */ + pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_ALLOW, + NULL, 1, &pjsip_info_method.name); + return status; } @@ -359,6 +371,7 @@ on_make_call_med_tp_complete(pjsua_call_id call_id, pjsip_dialog *dlg = call->async_call.dlg; unsigned options = 0; pjsip_tx_data *tdata; + pj_bool_t cb_called = PJ_FALSE; pj_status_t status = (info? info->status: PJ_SUCCESS); PJSUA_LOCK(); @@ -387,9 +400,16 @@ on_make_call_med_tp_complete(pjsua_call_id call_id, goto on_error; } - /* pjsua_media_channel_deinit() has been called. */ - if (call->async_call.med_ch_deinit) + /* pjsua_media_channel_deinit() has been called or + * call has been hung up. + */ + if (call->async_call.med_ch_deinit || + call->async_call.call_var.out_call.hangup) + { + PJ_LOG(4,(THIS_FILE, "Call has been hung up or media channel has " + "been deinitialized")); goto on_error; + } /* Create offer */ status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL, @@ -476,8 +496,7 @@ on_make_call_med_tp_complete(pjsua_call_id call_id, status = pjsip_inv_send_msg(inv, tdata); if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to send initial INVITE request", - status); + cb_called = PJ_TRUE; /* Upon failure to send first request, the invite * session would have been cleared. @@ -487,6 +506,7 @@ on_make_call_med_tp_complete(pjsua_call_id call_id, } /* Done. */ + call->med_ch_cb = NULL; pjsip_dlg_dec_lock(dlg); PJSUA_UNLOCK(); @@ -494,8 +514,11 @@ on_make_call_med_tp_complete(pjsua_call_id call_id, return PJ_SUCCESS; on_error: - if (inv == NULL && call_id != -1 && pjsua_var.ua_cfg.cb.on_call_state) + if (inv == NULL && call_id != -1 && !cb_called && + pjsua_var.ua_cfg.cb.on_call_state) + { (*pjsua_var.ua_cfg.cb.on_call_state)(call_id, NULL); + } if (dlg) { /* This may destroy the dialog */ @@ -511,6 +534,10 @@ on_error: pjsua_media_channel_deinit(call_id); } + call->med_ch_cb = NULL; + + pjsua_check_snd_dev_idle(); + PJSUA_UNLOCK(); return status; } @@ -547,35 +574,23 @@ static pj_status_t apply_call_setting(pjsua_call *call, pj_assert(opt->vid_cnt == 0); #endif + call->opt = *opt; + /* If call is established, reinit media channel */ if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED) { - pjsua_call_setting old_opt; + pjsip_role_e role = rem_sdp? PJSIP_ROLE_UAS : PJSIP_ROLE_UAC; pj_status_t status; - old_opt = call->opt; - call->opt = *opt; - - /* Reinit media channel when media count is changed or we are the - * answerer (as remote offer may 'extremely' modify the existing - * media session, e.g: media type order). - */ - if (rem_sdp || - opt->aud_cnt!=old_opt.aud_cnt || opt->vid_cnt!=old_opt.vid_cnt) - { - pjsip_role_e role = rem_sdp? PJSIP_ROLE_UAS : PJSIP_ROLE_UAC; - status = pjsua_media_channel_init(call->index, role, - call->secure_level, - call->inv->pool_prov, - rem_sdp, NULL, - PJ_FALSE, NULL); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Error re-initializing media channel", - status); - return status; - } + status = pjsua_media_channel_init(call->index, role, + call->secure_level, + call->inv->pool_prov, + rem_sdp, NULL, + PJ_FALSE, NULL); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error re-initializing media channel", + status); + return status; } - } else { - call->opt = *opt; } return PJ_SUCCESS; @@ -811,8 +826,8 @@ static pj_status_t process_incoming_call_replace(pjsua_call *call, { pjsip_inv_session *replaced_inv; struct pjsua_call *replaced_call; - pjsip_tx_data *tdata; - pj_status_t status; + pjsip_tx_data *tdata = NULL; + pj_status_t status = PJ_SUCCESS; /* Get the invite session in the dialog */ replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg); @@ -825,12 +840,29 @@ static pj_status_t process_incoming_call_replace(pjsua_call *call, pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index, call->index); - PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK", - call->index)); + if (replaced_call->inv->state <= PJSIP_INV_STATE_EARLY && + replaced_call->inv->role != PJSIP_ROLE_UAC) + { + if (replaced_call->last_code > 100 && replaced_call->last_code < 200) + { + pjsip_status_code code = replaced_call->last_code; + pj_str_t *text = &replaced_call->last_text; + + PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with %d/%.*s", + call->index, code, text->slen, text->ptr)); + + /* Answer the new call with last response in the replaced call */ + status = pjsip_inv_answer(call->inv, code, text, NULL, &tdata); + } + } else { + PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK", + call->index)); + + /* Answer the new call with 200 response */ + status = pjsip_inv_answer(call->inv, 200, NULL, NULL, &tdata); + } - /* Answer the new call with 200 response */ - status = pjsip_inv_answer(call->inv, 200, NULL, NULL, &tdata); - if (status == PJ_SUCCESS) + if (status == PJ_SUCCESS && tdata) status = pjsip_inv_send_msg(call->inv, tdata); if (status != PJ_SUCCESS) @@ -997,7 +1029,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) int acc_id; pjsua_call *call; int call_id = -1; - int sip_err_code; + int sip_err_code = PJSIP_SC_INTERNAL_SERVER_ERROR; pjmedia_sdp_session *offer=NULL; pj_status_t status; @@ -1182,7 +1214,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) options |= PJSIP_INV_SUPPORT_TIMER; if (pjsua_var.acc[acc_id].cfg.require_100rel == PJSUA_100REL_MANDATORY) options |= PJSIP_INV_REQUIRE_100REL; - if (pjsua_var.media_cfg.enable_ice) + if (pjsua_var.acc[acc_id].cfg.ice_cfg.enable_ice) options |= PJSIP_INV_SUPPORT_ICE; if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED) options |= PJSIP_INV_REQUIRE_TIMER; @@ -1488,6 +1520,7 @@ pj_status_t acquire_call(const char *title, pj_bool_t has_pjsua_lock = PJ_FALSE; pj_status_t status = PJ_SUCCESS; pj_time_val time_start, timeout; + pjsip_dialog *dlg = NULL; pj_gettimeofday(&time_start); timeout.sec = 0; @@ -1515,14 +1548,18 @@ pj_status_t acquire_call(const char *title, has_pjsua_lock = PJ_TRUE; call = &pjsua_var.calls[call_id]; + if (call->inv) + dlg = call->inv->dlg; + else + dlg = call->async_call.dlg; - if (call->inv == NULL) { + if (dlg == NULL) { PJSUA_UNLOCK(); PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title)); return PJSIP_ESESSIONTERMINATED; } - status = pjsip_dlg_try_inc_lock(call->inv->dlg); + status = pjsip_dlg_try_inc_lock(dlg); if (status != PJ_SUCCESS) { PJSUA_UNLOCK(); pj_thread_sleep(retry/10); @@ -1547,7 +1584,7 @@ pj_status_t acquire_call(const char *title, } *p_call = call; - *p_dlg = call->inv->dlg; + *p_dlg = dlg; return PJ_SUCCESS; } @@ -1992,7 +2029,7 @@ PJ_DEF(pj_status_t) pjsua_call_answer2(pjsua_call_id call_id, * answer code 183 or 2xx is issued */ if (!call->med_ch_cb && - (call->opt_inited || (code==183 && code/100==2)) && + (call->opt_inited || (code==183 || code/100==2)) && (!call->inv->neg || pjmedia_sdp_neg_get_state(call->inv->neg) == PJMEDIA_SDP_NEG_STATE_NULL)) @@ -2120,6 +2157,25 @@ PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id, if (status != PJ_SUCCESS) goto on_return; + /* If media transport creation is not yet completed, we will hangup + * the call in the media transport creation callback instead. + */ + if (call->med_ch_cb && !call->inv) { + PJ_LOG(4,(THIS_FILE, "Pending call %d hangup upon completion " + "of media transport", call_id)); + call->async_call.call_var.out_call.hangup = PJ_TRUE; + if (code == 0) + call->last_code = PJSIP_SC_REQUEST_TERMINATED; + else + call->last_code = (pjsip_status_code)code; + if (reason) { + pj_strncpy(&call->last_text, reason, + sizeof(call->last_text_buf_)); + } + + goto on_return; + } + if (code==0) { if (call->inv->state == PJSIP_INV_STATE_CONFIRMED) code = PJSIP_SC_OK; @@ -2156,11 +2212,10 @@ PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id, goto on_return; } - /* Stop lock codec timer, if it is active */ - if (call->lock_codec.reinv_timer.id) { - pjsip_endpt_cancel_timer(pjsua_var.endpt, - &call->lock_codec.reinv_timer); - call->lock_codec.reinv_timer.id = PJ_FALSE; + /* Stop reinvite timer, if it is active */ + if (call->reinv_timer.id) { + pjsua_cancel_timer(&call->reinv_timer); + call->reinv_timer.id = PJ_FALSE; } on_return: @@ -2337,7 +2392,7 @@ PJ_DEF(pj_status_t) pjsua_call_reinvite2(pjsua_call_id call_id, goto on_return; } - if ((call->opt.flag & PJSUA_CALL_UPDATE_CONTACT) & + if ((call->opt.flag & PJSUA_CALL_UPDATE_CONTACT) && pjsua_acc_is_valid(call->acc_id)) { new_contact = &pjsua_var.acc[call->acc_id].contact; @@ -2433,7 +2488,7 @@ PJ_DEF(pj_status_t) pjsua_call_update2(pjsua_call_id call_id, goto on_return; } - if ((call->opt.flag & PJSUA_CALL_UPDATE_CONTACT) & + if ((call->opt.flag & PJSUA_CALL_UPDATE_CONTACT) && pjsua_acc_is_valid(call->acc_id)) { new_contact = &pjsua_var.acc[call->acc_id].contact; @@ -2850,12 +2905,8 @@ PJ_DEF(void) pjsua_call_hangup_all(void) } -/* Proto */ -static pj_status_t perform_lock_codec(pjsua_call *call); - -/* Timer callback to send re-INVITE or UPDATE to lock codec */ -static void reinv_timer_cb(pj_timer_heap_t *th, - pj_timer_entry *entry) +/* Timer callback to send re-INVITE/UPDATE to lock codec or ICE update */ +static void reinv_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry) { pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data; pjsip_dialog *dlg; @@ -2864,21 +2915,27 @@ static void reinv_timer_cb(pj_timer_heap_t *th, PJ_UNUSED_ARG(th); - pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE; + pjsua_var.calls[call_id].reinv_timer.id = PJ_FALSE; + + pj_log_push_indent(); status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg); - if (status != PJ_SUCCESS) + if (status != PJ_SUCCESS) { + pj_log_pop_indent(); return; + } - status = perform_lock_codec(call); + process_pending_reinvite(call); pjsip_dlg_dec_lock(dlg); + + pj_log_pop_indent(); } /* Check if the specified format can be skipped in counting codecs */ static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m, - const pj_str_t *fmt) + const pj_str_t *fmt) { const pj_str_t STR_TEL = {"telephone-event", 15}; unsigned pt; @@ -2911,62 +2968,62 @@ static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m, } -/* Send re-INVITE or UPDATE with new SDP offer to select only one codec - * out of several codecs presented by callee in his answer. +/* Schedule check for the need of re-INVITE/UPDATE after media update, cases: + * - lock codec if remote answerer has given us more than one codecs + * - update ICE default transport address if it has changed after ICE + * connectivity check. */ -static pj_status_t perform_lock_codec(pjsua_call *call) +void pjsua_call_schedule_reinvite_check(pjsua_call *call, unsigned delay_ms) +{ + pj_time_val delay; + + /* Stop reinvite timer, if it is active */ + if (call->reinv_timer.id) + pjsua_cancel_timer(&call->reinv_timer); + + delay.sec = 0; + delay.msec = delay_ms; + pj_time_val_normalize(&delay); + call->reinv_timer.id = PJ_TRUE; + pjsua_schedule_timer(&call->reinv_timer, &delay); +} + + +/* Check if lock codec is needed */ +static pj_bool_t check_lock_codec(pjsua_call *call) { - const pj_str_t STR_UPDATE = {"UPDATE", 6}; - const pjmedia_sdp_session *local_sdp = NULL, *new_sdp; + const pjmedia_sdp_session *local_sdp, *remote_sdp; + pj_bool_t has_mult_fmt = PJ_FALSE; unsigned i; - pj_bool_t rem_can_update; - pj_bool_t need_lock_codec = PJ_FALSE; - pjsip_tx_data *tdata; pj_status_t status; - PJ_ASSERT_RETURN(call->lock_codec.reinv_timer.id==PJ_FALSE, - PJ_EINVALIDOP); + /* Check if lock codec is disabled */ + if (!pjsua_var.acc[call->acc_id].cfg.lock_codec) + return PJ_FALSE; - /* Verify if another SDP negotiation is in progress, e.g: session timer - * or another re-INVITE. - */ - if (call->inv==NULL || call->inv->neg==NULL || - pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE) - { - return PJMEDIA_SDPNEG_EINSTATE; - } + /* Check lock codec retry count */ + if (call->lock_codec.retry_cnt >= LOCK_CODEC_MAX_RETRY) + return PJ_FALSE; - /* Don't do this if call is disconnecting! */ - if (call->inv->state > PJSIP_INV_STATE_CONFIRMED || - call->inv->cause >= 200) - { - return PJ_EINVALIDOP; - } + /* Check if we are the answerer, we shouldn't need to lock codec */ + if (!call->inv->neg || !pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) + return PJ_FALSE; - /* Verify if another SDP negotiation has been completed by comparing - * the SDP version. - */ + /* Check if remote answerer has given us more than one codecs. */ status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp); if (status != PJ_SUCCESS) - return status; - if (local_sdp->origin.version > call->lock_codec.sdp_ver) - return PJMEDIA_SDP_EINVER; - - PJ_LOG(3, (THIS_FILE, "Updating media session to use only one codec..")); - - /* Update the new offer so it contains only a codec. Note that formats - * order in the offer should have been matched to the answer, so we can - * just directly update the offer without looking-up the answer. - */ - new_sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, local_sdp); + return PJ_FALSE; + status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp); + if (status != PJ_SUCCESS) + return PJ_FALSE; - for (i = 0; i < call->med_cnt; ++i) { - unsigned j = 0, codec_cnt = 0; - const pjmedia_sdp_media *ref_m; - pjmedia_sdp_media *m; + for (i = 0; i < call->med_cnt && !has_mult_fmt; ++i) { pjsua_call_media *call_med = &call->media[i]; + const pjmedia_sdp_media *rem_m, *loc_m; + unsigned codec_cnt = 0; + unsigned j; - /* Verify if media is deactivated */ + /* Skip this if the media is inactive or error */ if (call_med->state == PJSUA_CALL_MEDIA_NONE || call_med->state == PJSUA_CALL_MEDIA_ERROR || call_med->dir == PJMEDIA_DIR_NONE) @@ -2974,192 +3031,336 @@ static pj_status_t perform_lock_codec(pjsua_call *call) continue; } - ref_m = local_sdp->media[i]; - m = new_sdp->media[i]; - - /* Verify that media must be active. */ - pj_assert(ref_m->desc.port); + /* Remote may answer with less media lines. */ + if (i >= remote_sdp->media_count) + continue; - while (j < m->desc.fmt_count) { - pjmedia_sdp_attr *a; - pj_str_t *fmt = &m->desc.fmt[j]; + rem_m = remote_sdp->media[i]; + loc_m = local_sdp->media[i]; - if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) { - ++j; - continue; - } + /* Verify that media must be active. */ + pj_assert(loc_m->desc.port && rem_m->desc.port); - /* Remove format */ - a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt); - if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a); - a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt); - if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a); - pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]), - m->desc.fmt_count, j); - --m->desc.fmt_count; + /* Count the formats in the answer. */ + for (j=0; jdesc.fmt_count && codec_cnt <= 1; ++j) { + if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[j]) && ++codec_cnt > 1) + has_mult_fmt = PJ_TRUE; } - - need_lock_codec |= (ref_m->desc.fmt_count > m->desc.fmt_count); } - /* Last check if SDP trully needs to be updated. It is possible that OA - * negotiations have completed and SDP has changed but we didn't - * increase the SDP version (should not happen!). - */ - if (!need_lock_codec) - return PJ_SUCCESS; + /* Reset retry count when remote answer has one codec */ + if (!has_mult_fmt) + call->lock_codec.retry_cnt = 0; - /* Send UPDATE or re-INVITE */ - rem_can_update = pjsip_dlg_remote_has_cap(call->inv->dlg, - PJSIP_H_ALLOW, - NULL, &STR_UPDATE) == - PJSIP_DIALOG_CAP_SUPPORTED; - if (rem_can_update) { - status = pjsip_inv_update(call->inv, NULL, new_sdp, &tdata); - } else { - status = pjsip_inv_reinvite(call->inv, NULL, new_sdp, &tdata); - } + return has_mult_fmt; +} - if (status==PJ_EINVALIDOP && - ++call->lock_codec.retry_cnt <= LOCK_CODEC_MAX_RETRY) - { - /* Ups, let's reschedule again */ - pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL}; - pj_time_val_normalize(&delay); - call->lock_codec.reinv_timer.id = PJ_TRUE; - pjsip_endpt_schedule_timer(pjsua_var.endpt, - &call->lock_codec.reinv_timer, &delay); - return status; - } else if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE to lock codec", - status); - return status; - } +/* Check if ICE setup is complete and if it needs to send reinvite */ +static pj_bool_t check_ice_complete(pjsua_call *call, pj_bool_t *need_reinv) +{ + pj_bool_t ice_need_reinv = PJ_FALSE; + pj_bool_t ice_complete = PJ_TRUE; + unsigned i; - /* Send the UPDATE/re-INVITE request */ - status = pjsip_inv_send_msg(call->inv, tdata); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE in lock codec", - status); - return status; - } + /* Check if ICE setup is complete and if it needs reinvite */ + for (i = 0; i < call->med_cnt; ++i) { + pjsua_call_media *call_med = &call->media[i]; + pjmedia_transport_info tpinfo; + pjmedia_ice_transport_info *ice_info; + + if (call_med->tp_st == PJSUA_MED_TP_NULL || + call_med->tp_st == PJSUA_MED_TP_DISABLED || + call_med->state == PJSUA_CALL_MEDIA_ERROR) + { + continue; + } + + pjmedia_transport_info_init(&tpinfo); + pjmedia_transport_get_info(call_med->tp, &tpinfo); + ice_info = (pjmedia_ice_transport_info*) + pjmedia_transport_info_get_spc_info( + &tpinfo, PJMEDIA_TRANSPORT_TYPE_ICE); + + /* Check if ICE is active */ + if (!ice_info || !ice_info->active) + continue; - return status; + /* Check if ICE setup not completed yet */ + if (ice_info->sess_state < PJ_ICE_STRANS_STATE_RUNNING) { + ice_complete = PJ_FALSE; + break; + } + + /* Check if ICE needs to send reinvite */ + if (!ice_need_reinv && + ice_info->sess_state == PJ_ICE_STRANS_STATE_RUNNING && + ice_info->role == PJ_ICE_SESS_ROLE_CONTROLLING) + { + pjsua_ice_config *cfg=&pjsua_var.acc[call->acc_id].cfg.ice_cfg; + if ((cfg->ice_always_update && !call->reinv_ice_sent) || + pj_sockaddr_cmp(&tpinfo.sock_info.rtp_addr_name, + &call_med->rtp_addr)) + { + ice_need_reinv = PJ_TRUE; + } + } + } + + if (ice_complete && need_reinv) + *need_reinv = ice_need_reinv; + + return ice_complete; } -/* Check if remote answerer has given us more than one codecs. If so, - * create another offer with one codec only to lock down the codec. - */ -static pj_status_t lock_codec(pjsua_call *call) +/* Check and send reinvite for lock codec and ICE update */ +static pj_status_t process_pending_reinvite(pjsua_call *call) { + const pj_str_t ST_UPDATE = {"UPDATE", 6}; + pj_pool_t *pool = call->inv->pool_prov; pjsip_inv_session *inv = call->inv; - const pjmedia_sdp_session *local_sdp, *remote_sdp; - pj_time_val delay = {0, 0}; - const pj_str_t st_update = {"UPDATE", 6}; + pj_bool_t ice_need_reinv; + pj_bool_t ice_completed; + pj_bool_t need_lock_codec; + pj_bool_t rem_can_update; + pjmedia_sdp_session *new_offer; + pjsip_tx_data *tdata; unsigned i; - pj_bool_t has_mult_fmt = PJ_FALSE; pj_status_t status; - /* Stop lock codec timer, if it is active */ - if (call->lock_codec.reinv_timer.id) { - pjsip_endpt_cancel_timer(pjsua_var.endpt, - &call->lock_codec.reinv_timer); - call->lock_codec.reinv_timer.id = PJ_FALSE; + /* Verify if another SDP negotiation is in progress, e.g: session timer + * or another re-INVITE. + */ + if (inv==NULL || inv->neg==NULL || + pjmedia_sdp_neg_get_state(inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE) + { + return PJMEDIA_SDPNEG_EINSTATE; } - /* Skip this if we are the answerer */ - if (!inv->neg || !pjmedia_sdp_neg_was_answer_remote(inv->neg)) { - return PJ_SUCCESS; + /* Don't do this if call is disconnecting! */ + if (inv->state > PJSIP_INV_STATE_CONFIRMED || inv->cause >= 200) + { + return PJ_EINVALIDOP; } /* Delay this when the SDP negotiation done in call state EARLY and * remote does not support UPDATE method. */ if (inv->state == PJSIP_INV_STATE_EARLY && - pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)!= + pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &ST_UPDATE)!= PJSIP_DIALOG_CAP_SUPPORTED) { - call->lock_codec.pending = PJ_TRUE; - return PJ_SUCCESS; + call->reinv_pending = PJ_TRUE; + return PJ_EPENDING; } - status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp); - if (status != PJ_SUCCESS) - return status; - status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp); - if (status != PJ_SUCCESS) - return status; + /* Check if ICE setup is complete and if it needs reinvite */ + ice_completed = check_ice_complete(call, &ice_need_reinv); + if (!ice_completed) + return PJ_EPENDING; - /* Find multiple codecs answer in all media */ - for (i = 0; i < call->med_cnt; ++i) { - pjsua_call_media *call_med = &call->media[i]; - const pjmedia_sdp_media *rem_m, *loc_m; - unsigned codec_cnt = 0; + /* Check if we need to lock codec */ + need_lock_codec = check_lock_codec(call); - /* Skip this if the media is inactive or error */ - if (call_med->state == PJSUA_CALL_MEDIA_NONE || - call_med->state == PJSUA_CALL_MEDIA_ERROR || - call_med->dir == PJMEDIA_DIR_NONE) - { - continue; - } + /* Check if reinvite is really needed */ + if (!need_lock_codec && !ice_need_reinv) + return PJ_SUCCESS; - /* Remote may answer with less media lines. */ - if (i >= remote_sdp->media_count) - continue; + + /* Okay! So we need to send re-INVITE/UPDATE */ - rem_m = remote_sdp->media[i]; - loc_m = local_sdp->media[i]; + /* Check if remote support UPDATE */ + rem_can_update = pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, + &ST_UPDATE) == + PJSIP_DIALOG_CAP_SUPPORTED; - /* Verify that media must be active. */ - pj_assert(loc_m->desc.port && rem_m->desc.port); + /* Logging stuff */ + { + const char *ST_ICE_UPDATE = "ICE transport address after " + "ICE negotiation"; + const char *ST_LOCK_CODEC = "media session to use only one codec"; + PJ_LOG(4,(THIS_FILE, "Call %d sending %s for updating %s%s%s", + call->index, + (rem_can_update? "UPDATE" : "re-INVITE"), + (ice_need_reinv? ST_ICE_UPDATE : ST_LOCK_CODEC), + (ice_need_reinv && need_lock_codec? " and " : ""), + (ice_need_reinv && need_lock_codec? ST_LOCK_CODEC : "") + )); + } + + /* Generate SDP re-offer */ + status = pjsua_media_channel_create_sdp(call->index, pool, NULL, + &new_offer, NULL); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to create local SDP", status); + return status; + } - /* Count the formats in the answer. */ - if (rem_m->desc.fmt_count==1) { - codec_cnt = 1; - } else { - unsigned j; - for (j=0; jdesc.fmt_count && codec_cnt <= 1; ++j) { - if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[j])) - ++codec_cnt; - } + /* Update the new offer so it contains only a codec. Note that + * SDP nego has removed unmatched codecs from the offer and the codec + * order in the offer has been matched to the answer, so we'll override + * the codecs in the just generated SDP with the ones from the active + * local SDP and leave just one codec for the next SDP re-offer. + */ + if (need_lock_codec) { + const pjmedia_sdp_session *ref_sdp; + + /* Get local active SDP as reference */ + status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &ref_sdp); + if (status != PJ_SUCCESS) + return status; + + /* Verify media count. Note that remote may add/remove media line + * in the answer. When answer has less media, it must have been + * handled by pjsua_media_channel_update() as disabled media. + * When answer has more media, it must have been ignored (treated + * as non-exist) anywhere. Local media count should not be updated + * at this point, as modifying media count operation (i.e: reinvite, + * update, vid_set_strm) is currently blocking, protected with + * dialog mutex, and eventually reset SDP nego state to LOCAL OFFER. + */ + if (call->med_cnt != ref_sdp->media_count || + ref_sdp->media_count != new_offer->media_count) + { + /* Anyway, just in case, let's just return error */ + return PJMEDIA_SDPNEG_EINSTATE; } - if (codec_cnt > 1) { - has_mult_fmt = PJ_TRUE; - break; + for (i = 0; i < call->med_cnt; ++i) { + unsigned j, codec_cnt = 0; + const pjmedia_sdp_media *ref_m = ref_sdp->media[i]; + pjmedia_sdp_media *m = new_offer->media[i]; + pjsua_call_media *call_med = &call->media[i]; + + /* Verify if media is deactivated */ + if (call_med->state == PJSUA_CALL_MEDIA_NONE || + call_med->state == PJSUA_CALL_MEDIA_ERROR || + call_med->dir == PJMEDIA_DIR_NONE) + { + continue; + } + + /* Reset formats */ + m->desc.fmt_count = 0; + pjmedia_sdp_attr_remove_all(&m->attr_count, m->attr, "rtpmap"); + pjmedia_sdp_attr_remove_all(&m->attr_count, m->attr, "fmtp"); + + /* Copy only the first format + any non-AV formats from + * the active local SDP. + */ + for (j = 0; j < ref_m->desc.fmt_count; ++j) { + const pj_str_t *fmt = &ref_m->desc.fmt[j]; + + if (is_non_av_fmt(ref_m, fmt) || (++codec_cnt == 1)) { + pjmedia_sdp_attr *a; + + m->desc.fmt[m->desc.fmt_count++] = *fmt; + a = pjmedia_sdp_attr_find2(ref_m->attr_count, ref_m->attr, + "rtpmap", fmt); + if (a) pjmedia_sdp_attr_add(&m->attr_count, m->attr, a); + a = pjmedia_sdp_attr_find2(ref_m->attr_count, ref_m->attr, + "fmtp", fmt); + if (a) pjmedia_sdp_attr_add(&m->attr_count, m->attr, a); + } + } } } - /* Each media in the answer already contains single codec. */ - if (!has_mult_fmt) { - call->lock_codec.retry_cnt = 0; - return PJ_SUCCESS; - } + /* Put back original direction and "c=0.0.0.0" line */ + { + const pjmedia_sdp_session *cur_sdp; + + /* Get local active SDP */ + status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &cur_sdp); + if (status != PJ_SUCCESS) + return status; - /* Remote keeps answering with multiple codecs, let's just give up - * locking codec to avoid infinite retry loop. - */ - if (++call->lock_codec.retry_cnt > LOCK_CODEC_MAX_RETRY) - return PJ_SUCCESS; + /* Make sure media count has not been changed */ + if (call->med_cnt != cur_sdp->media_count) + return PJMEDIA_SDPNEG_EINSTATE; + + for (i = 0; i < call->med_cnt; ++i) { + const pjmedia_sdp_media *m = cur_sdp->media[i]; + pjmedia_sdp_media *new_m = new_offer->media[i]; + pjsua_call_media *call_med = &call->media[i]; + pjmedia_sdp_attr *a = NULL; + + /* Update direction to the current dir */ + pjmedia_sdp_media_remove_all_attr(new_m, "sendrecv"); + pjmedia_sdp_media_remove_all_attr(new_m, "sendonly"); + pjmedia_sdp_media_remove_all_attr(new_m, "recvonly"); + pjmedia_sdp_media_remove_all_attr(new_m, "inactive"); + + if (call_med->dir == PJMEDIA_DIR_ENCODING_DECODING) { + a = pjmedia_sdp_attr_create(pool, "sendrecv", NULL); + } else if (call_med->dir == PJMEDIA_DIR_ENCODING) { + a = pjmedia_sdp_attr_create(pool, "sendonly", NULL); + } else if (call_med->dir == PJMEDIA_DIR_DECODING) { + a = pjmedia_sdp_attr_create(pool, "recvonly", NULL); + } else { + const pjmedia_sdp_conn *conn; + a = pjmedia_sdp_attr_create(pool, "inactive", NULL); + + /* Also check if the original c= line address is zero */ + conn = m->conn; + if (!conn) + conn = cur_sdp->conn; + if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 || + pj_strcmp2(&conn->addr, "0")==0) + { + if (!new_m->conn) { + new_m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn); + } + + if (pj_strcmp2(&new_m->conn->addr, "0.0.0.0")) { + new_m->conn->net_type = pj_str("IN"); + new_m->conn->addr_type = pj_str("IP4"); + new_m->conn->addr = pj_str("0.0.0.0"); + } + } + } - PJ_LOG(4, (THIS_FILE, "Got answer with multiple codecs, scheduling " - "updating media session to use only one codec..")); + pj_assert(a); + pjmedia_sdp_media_add_attr(new_m, a); + } + } - call->lock_codec.sdp_ver = local_sdp->origin.version; + + if (rem_can_update) { + status = pjsip_inv_update(inv, NULL, new_offer, &tdata); + } else { + status = pjsip_inv_reinvite(inv, NULL, new_offer, &tdata); + } - /* Can't send UPDATE or re-INVITE now, so just schedule it immediately. - * See: https://trac.pjsip.org/repos/ticket/1149 - */ - pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE, - (void*)(pj_size_t)call->index, - &reinv_timer_cb); - pjsip_endpt_schedule_timer(pjsua_var.endpt, - &call->lock_codec.reinv_timer, &delay); + if (status==PJ_EINVALIDOP && + ++call->lock_codec.retry_cnt < LOCK_CODEC_MAX_RETRY) + { + /* Ups, let's reschedule again */ + pjsua_call_schedule_reinvite_check(call, LOCK_CODEC_RETRY_INTERVAL); + return PJ_SUCCESS; + } else if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE", + status); + return status; + } + /* Send the UPDATE/re-INVITE request */ + status = pjsip_inv_send_msg(inv, tdata); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE", + status); + return status; + } + + /* Update flags */ + if (ice_need_reinv) + call->reinv_ice_sent = PJ_TRUE; + if (need_lock_codec) + ++call->lock_codec.retry_cnt; + return PJ_SUCCESS; } + /* * This callback receives notification from invite session when the * session state has changed. @@ -3194,16 +3395,12 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv, case PJSIP_INV_STATE_CONFIRMED: pj_gettimeofday(&call->conn_time); - /* See if lock codec was pended as media update was done in the + /* See if auto reinvite was pended as media update was done in the * EARLY state and remote does not support UPDATE. */ - if (call->lock_codec.pending) { - pj_status_t status; - status = lock_codec(call); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to lock codec", status); - } - call->lock_codec.pending = PJ_FALSE; + if (call->reinv_pending) { + call->reinv_pending = PJ_FALSE; + pjsua_call_schedule_reinvite_check(call, 0); } break; case PJSIP_INV_STATE_DISCONNECTED: @@ -3225,11 +3422,10 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv, sizeof(call->last_text_buf_)); } - /* Stop lock codec timer, if it is active */ - if (call->lock_codec.reinv_timer.id) { - pjsip_endpt_cancel_timer(pjsua_var.endpt, - &call->lock_codec.reinv_timer); - call->lock_codec.reinv_timer.id = PJ_FALSE; + /* Stop reinvite timer, if it is active */ + if (call->reinv_timer.id) { + pjsua_cancel_timer(&call->reinv_timer); + call->reinv_timer.id = PJ_FALSE; } break; default: @@ -3307,6 +3503,15 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv, } } + /* Ticket #1627: Invoke on_call_tsx_state() when call is disconnected. */ + if (inv->state == PJSIP_INV_STATE_DISCONNECTED && + e->type == PJSIP_EVENT_TSX_STATE && + call->inv && + pjsua_var.ua_cfg.cb.on_call_tsx_state) + { + (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, + e->body.tsx_state.tsx, e); + } if (pjsua_var.ua_cfg.cb.on_call_state) (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e); @@ -3512,10 +3717,7 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv, } /* Ticket #476: make sure only one codec is specified in the answer. */ - status = lock_codec(call); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to lock codec", status); - } + pjsua_call_schedule_reinvite_check(call, 0); /* Call application callback, if any */ if (pjsua_var.ua_cfg.cb.on_call_media_state) @@ -4221,11 +4423,7 @@ static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv, goto on_return; if (call->inv == NULL) { - /* Shouldn't happen. It happens only when we don't terminate the - * server subscription caused by REFER after the call has been - * transfered (and this call has been disconnected), and we - * receive another REFER for this call. - */ + /* Call has been disconnected. */ goto on_return; } diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index f4f9508..e352238 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -1,4 +1,4 @@ -/* $Id: pjsua_core.c 4173 2012-06-20 10:39:05Z ming $ */ +/* $Id: pjsua_core.c 4370 2013-02-26 05:30:00Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -195,13 +195,66 @@ PJ_DEF(void) pjsua_transport_config_default(pjsua_transport_config *cfg) PJ_DEF(void) pjsua_transport_config_dup(pj_pool_t *pool, pjsua_transport_config *dst, const pjsua_transport_config *src) +{ + pj_memcpy(dst, src, sizeof(*src)); + pj_strdup(pool, &dst->public_addr, &src->public_addr); + pj_strdup(pool, &dst->bound_addr, &src->bound_addr); +} + +PJ_DEF(void) pjsua_ice_config_from_media_config( pj_pool_t *pool, + pjsua_ice_config *dst, + const pjsua_media_config *src) +{ + PJ_UNUSED_ARG(pool); + + dst->enable_ice = src->enable_ice; + dst->ice_max_host_cands = src->ice_max_host_cands; + dst->ice_opt = src->ice_opt; + dst->ice_no_rtcp = src->ice_no_rtcp; + dst->ice_always_update = src->ice_always_update; +} + +PJ_DEF(void) pjsua_ice_config_dup( pj_pool_t *pool, + pjsua_ice_config *dst, + const pjsua_ice_config *src) { PJ_UNUSED_ARG(pool); pj_memcpy(dst, src, sizeof(*src)); } +PJ_DEF(void) pjsua_turn_config_from_media_config(pj_pool_t *pool, + pjsua_turn_config *dst, + const pjsua_media_config *src) +{ + dst->enable_turn = src->enable_turn; + dst->turn_conn_type = src->turn_conn_type; + if (pool == NULL) { + dst->turn_server = src->turn_server; + dst->turn_auth_cred = src->turn_auth_cred; + } else { + if (pj_stricmp(&dst->turn_server, &src->turn_server)) + pj_strdup(pool, &dst->turn_server, &src->turn_server); + pj_stun_auth_cred_dup(pool, &dst->turn_auth_cred, + &src->turn_auth_cred); + } +} + +PJ_DEF(void) pjsua_turn_config_dup(pj_pool_t *pool, + pjsua_turn_config *dst, + const pjsua_turn_config *src) +{ + pj_memcpy(dst, src, sizeof(*src)); + if (pool) { + pj_strdup(pool, &dst->turn_server, &src->turn_server); + pj_stun_auth_cred_dup(pool, &dst->turn_auth_cred, + &src->turn_auth_cred); + } +} + PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg) { + pjsua_media_config med_cfg; + pj_bzero(cfg, sizeof(*cfg)); cfg->reg_timeout = PJSUA_REG_INTERVAL; @@ -215,6 +268,7 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg) cfg->require_100rel = pjsua_var.ua_cfg.require_100rel; cfg->use_timer = pjsua_var.ua_cfg.use_timer; cfg->timer_setting = pjsua_var.ua_cfg.timer_setting; + cfg->lock_codec = 1; cfg->ka_interval = 15; cfg->ka_data = pj_str("\r\n"); cfg->vid_cap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV; @@ -223,6 +277,11 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg) pjmedia_vid_stream_rc_config_default(&cfg->vid_stream_rc_cfg); #endif pjsua_transport_config_default(&cfg->rtp_cfg); + + pjsua_media_config_default(&med_cfg); + pjsua_ice_config_from_media_config(NULL, &cfg->ice_cfg, &med_cfg); + pjsua_turn_config_from_media_config(NULL, &cfg->turn_cfg, &med_cfg); + cfg->use_srtp = pjsua_var.ua_cfg.use_srtp; cfg->srtp_secure_signaling = pjsua_var.ua_cfg.srtp_secure_signaling; cfg->srtp_optional_dup_offer = pjsua_var.ua_cfg.srtp_optional_dup_offer; @@ -266,6 +325,7 @@ PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg) cfg->snd_auto_close_time = 1; cfg->ice_max_host_cands = -1; + cfg->ice_always_update = PJ_TRUE; pj_ice_sess_options_default(&cfg->ice_opt); cfg->turn_conn_type = PJ_TURN_TP_UDP; @@ -898,7 +958,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, if (pjsua_var.ua_cfg.force_lr) { pjsip_sip_uri *sip_url; if (!PJSIP_URI_SCHEME_IS_SIP(r->name_addr.uri) && - !PJSIP_URI_SCHEME_IS_SIP(r->name_addr.uri)) + !PJSIP_URI_SCHEME_IS_SIPS(r->name_addr.uri)) { status = PJSIP_EINVALIDSCHEME; goto on_error; @@ -1017,7 +1077,7 @@ static void busy_sleep(unsigned msec) { pj_time_val timeout, now; - pj_gettimeofday(&timeout); + pj_gettickcount(&timeout); timeout.msec += msec; pj_time_val_normalize(&timeout); @@ -1026,15 +1086,21 @@ static void busy_sleep(unsigned msec) i = msec / 10; while (pjsua_handle_events(10) > 0 && i > 0) --i; - pj_gettimeofday(&now); + pj_gettickcount(&now); } while (PJ_TIME_VAL_LT(now, timeout)); } -/* Internal function to destroy STUN resolution session - * (pj_stun_resolve). - */ +static void stun_resolve_add_ref(pjsua_stun_resolve *sess) +{ + ++sess->ref_cnt; +} + static void destroy_stun_resolve(pjsua_stun_resolve *sess) { + sess->destroy_flag = PJ_TRUE; + if (sess->ref_cnt > 0) + return; + PJSUA_LOCK(); pj_list_erase(sess); PJSUA_UNLOCK(); @@ -1043,6 +1109,14 @@ static void destroy_stun_resolve(pjsua_stun_resolve *sess) pj_pool_release(sess->pool); } +static void stun_resolve_dec_ref(pjsua_stun_resolve *sess) +{ + --sess->ref_cnt; + if (sess->ref_cnt <= 0 && sess->destroy_flag) + destroy_stun_resolve(sess); +} + + /* This is the internal function to be called when STUN resolution * session (pj_stun_resolve) has completed. */ @@ -1050,11 +1124,15 @@ static void stun_resolve_complete(pjsua_stun_resolve *sess) { pj_stun_resolve_result result; + if (sess->has_result) + goto on_return; + pj_bzero(&result, sizeof(result)); result.token = sess->token; result.status = sess->status; result.name = sess->srv[sess->idx]; pj_memcpy(&result.addr, &sess->addr, sizeof(result.addr)); + sess->has_result = PJ_TRUE; if (result.status == PJ_SUCCESS) { char addr[PJ_INET6_ADDRSTRLEN+10]; @@ -1070,8 +1148,11 @@ static void stun_resolve_complete(pjsua_stun_resolve *sess) PJ_LOG(1,(THIS_FILE, "STUN resolution failed: %s", errmsg)); } + stun_resolve_add_ref(sess); sess->cb(&result); + stun_resolve_dec_ref(sess); +on_return: if (!sess->blocking) { destroy_stun_resolve(sess); } @@ -1133,22 +1214,27 @@ static pj_bool_t test_stun_on_status(pj_stun_sock *stun_sock, */ static void resolve_stun_entry(pjsua_stun_resolve *sess) { + stun_resolve_add_ref(sess); + /* Loop while we have entry to try */ for (; sess->idx < sess->count; ++sess->idx) { const int af = pj_AF_INET(); + char target[64]; pj_str_t hostpart; pj_uint16_t port; pj_stun_sock_cb stun_sock_cb; pj_assert(sess->idx < sess->count); + pj_ansi_snprintf(target, sizeof(target), "%.*s", + (int)sess->srv[sess->idx].slen, + sess->srv[sess->idx].ptr); + /* Parse the server entry into host:port */ sess->status = pj_sockaddr_parse2(af, 0, &sess->srv[sess->idx], &hostpart, &port, NULL); if (sess->status != PJ_SUCCESS) { - PJ_LOG(2,(THIS_FILE, "Invalid STUN server entry %.*s", - (int)sess->srv[sess->idx].slen, - sess->srv[sess->idx].ptr)); + PJ_LOG(2,(THIS_FILE, "Invalid STUN server entry %s", target)); continue; } @@ -1158,10 +1244,8 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) pj_assert(sess->stun_sock == NULL); - PJ_LOG(4,(THIS_FILE, "Trying STUN server %.*s (%d of %d)..", - (int)sess->srv[sess->idx].slen, - sess->srv[sess->idx].ptr, - sess->idx+1, sess->count)); + PJ_LOG(4,(THIS_FILE, "Trying STUN server %s (%d of %d)..", + target, sess->idx+1, sess->count)); /* Use STUN_sock to test this entry */ pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb)); @@ -1173,9 +1257,8 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(sess->status, errmsg, sizeof(errmsg)); PJ_LOG(4,(THIS_FILE, - "Error creating STUN socket for %.*s: %s", - (int)sess->srv[sess->idx].slen, - sess->srv[sess->idx].ptr, errmsg)); + "Error creating STUN socket for %s: %s", + target, errmsg)); continue; } @@ -1186,19 +1269,20 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(sess->status, errmsg, sizeof(errmsg)); PJ_LOG(4,(THIS_FILE, - "Error starting STUN socket for %.*s: %s", - (int)sess->srv[sess->idx].slen, - sess->srv[sess->idx].ptr, errmsg)); + "Error starting STUN socket for %s: %s", + target, errmsg)); - pj_stun_sock_destroy(sess->stun_sock); - sess->stun_sock = NULL; + if (sess->stun_sock) { + pj_stun_sock_destroy(sess->stun_sock); + sess->stun_sock = NULL; + } continue; } /* Done for now, testing will resume/complete asynchronously in * stun_sock_cb() */ - return; + goto on_return; } if (sess->idx >= sess->count) { @@ -1207,6 +1291,9 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) sess->status = PJ_EUNKNOWN); stun_resolve_complete(sess); } + +on_return: + stun_resolve_dec_ref(sess); } @@ -1837,6 +1924,8 @@ static pj_status_t create_sip_udp_sock(int af, pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port); } else if (stun_srv.slen) { + pjstun_setting stun_opt; + /* * STUN is specified, resolve the address with STUN. */ @@ -1846,10 +1935,13 @@ static pj_status_t create_sip_udp_sock(int af, return PJ_EAFNOTSUP; } - status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock, - &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port), - &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port), - &p_pub_addr->ipv4); + pj_bzero(&stun_opt, sizeof(stun_opt)); + stun_opt.use_stun2 = pjsua_var.ua_cfg.stun_map_use_stun2; + stun_opt.srv1 = stun_opt.srv2 = stun_srv; + stun_opt.port1 = stun_opt.port2 = + pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port); + status = pjstun_get_mapped_addr2(&pjsua_var.cp.factory, &stun_opt, + 1, &sock, &p_pub_addr->ipv4); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error contacting STUN server", status); pj_sock_close(sock); @@ -1977,8 +2069,10 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, pjsua_transport_config config; pjsip_tpfactory *tcp; pjsip_tcp_transport_cfg tcp_cfg; + int af; - pjsip_tcp_transport_cfg_default(&tcp_cfg, pj_AF_INET()); + af = (type==PJSIP_TRANSPORT_TCP6) ? pj_AF_INET6() : pj_AF_INET(); + pjsip_tcp_transport_cfg_default(&tcp_cfg, af); /* Supply default config if it's not specified */ if (cfg == NULL) { @@ -2028,14 +2122,15 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, #endif /* PJ_HAS_TCP */ #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0 - } else if (type == PJSIP_TRANSPORT_TLS) { + } else if (type == PJSIP_TRANSPORT_TLS || type == PJSIP_TRANSPORT_TLS6) { /* * Create TLS transport. */ pjsua_transport_config config; pjsip_host_port a_name; pjsip_tpfactory *tls; - pj_sockaddr_in local_addr; + pj_sockaddr local_addr; + int af; /* Supply default config if it's not specified */ if (cfg == NULL) { @@ -2045,13 +2140,15 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, } /* Init local address */ - pj_sockaddr_in_init(&local_addr, 0, 0); + af = (type==PJSIP_TRANSPORT_TLS) ? pj_AF_INET() : pj_AF_INET6(); + pj_sockaddr_init(af, &local_addr, NULL, 0); if (cfg->port) - local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port); + pj_sockaddr_set_port(&local_addr, (pj_uint16_t)cfg->port); if (cfg->bound_addr.slen) { - status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr); + status = pj_sockaddr_set_str_addr(af, &local_addr, + &cfg->bound_addr); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to resolve transport bound address", @@ -2065,9 +2162,9 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, if (cfg->public_addr.slen) a_name.host = cfg->public_addr; - status = pjsip_tls_transport_start(pjsua_var.endpt, - &cfg->tls_setting, - &local_addr, &a_name, 1, &tls); + status = pjsip_tls_transport_start2(pjsua_var.endpt, + &cfg->tls_setting, + &local_addr, &a_name, 1, &tls); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating SIP TLS listener", status); @@ -2087,7 +2184,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, } /* Set transport state callback */ - if (pjsua_var.ua_cfg.cb.on_transport_state) { + { pjsip_tp_state_callback tpcb; pjsip_tpmgr *tpmgr; @@ -2743,7 +2840,7 @@ pj_status_t normalize_route_uri(pj_pool_t *pool, pj_str_t *uri) } if (!PJSIP_URI_SCHEME_IS_SIP(uri_obj) && - !PJSIP_URI_SCHEME_IS_SIP(uri_obj)) + !PJSIP_URI_SCHEME_IS_SIPS(uri_obj)) { PJ_LOG(1,(THIS_FILE, "Route URI must be SIP URI: %.*s", (int)uri->slen, uri->ptr)); @@ -2804,6 +2901,7 @@ PJ_DEF(void) pjsua_dump(pj_bool_t detail) PJ_LOG(3,(THIS_FILE, "Dumping media transports:")); for (i=0; iacc_id].cfg; + /* Dump the media transports in this call */ for (j = 0; j < tp_cnt; ++j) { pjmedia_transport_info tpinfo; @@ -2837,7 +2937,7 @@ PJ_DEF(void) pjsua_dump(pj_bool_t detail) pjmedia_transport_info_init(&tpinfo); pjmedia_transport_get_info(tp[j], &tpinfo); PJ_LOG(3,(THIS_FILE, " %s: %s", - (pjsua_var.media_cfg.enable_ice ? "ICE" : "UDP"), + (acc_cfg->ice_cfg.enable_ice ? "ICE" : "UDP"), pj_sockaddr_print(&tpinfo.sock_info.rtp_addr_name, addr_buf, sizeof(addr_buf), 3))); diff --git a/pjsip/src/pjsua-lib/pjsua_dump.c b/pjsip/src/pjsua-lib/pjsua_dump.c index 0b23a97..a24b71a 100644 --- a/pjsip/src/pjsua-lib/pjsua_dump.c +++ b/pjsip/src/pjsua-lib/pjsua_dump.c @@ -1,4 +1,4 @@ -/* $Id: pjsua_dump.c 4085 2012-04-25 07:45:22Z nanang $ */ +/* $Id: pjsua_dump.c 4300 2012-11-26 02:04:17Z ming $ */ /* * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com) * @@ -212,6 +212,10 @@ static unsigned dump_media_stat(const char *indent, /* Dump media session */ +#if PJSUA_MEDIA_HAS_PJMEDIA || \ + (PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO && \ + PJSUA_THIRD_PARTY_STREAM_HAS_GET_STAT) + static void dump_media_session(const char *indent, char *buf, unsigned maxlen, pjsua_call *call) @@ -858,6 +862,24 @@ static void dump_media_session(const char *indent, } } +#else /* PJSUA_MEDIA_HAS_PJMEDIA || + (PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO && + PJSUA_THIRD_PARTY_STREAM_HAS_GET_STAT) */ + +static void dump_media_session(const char *indent, + char *buf, unsigned maxlen, + pjsua_call *call) +{ + PJ_UNUSED_ARG(indent); + PJ_UNUSED_ARG(buf); + PJ_UNUSED_ARG(maxlen); + PJ_UNUSED_ARG(call); +} + +#endif /* PJSUA_MEDIA_HAS_PJMEDIA || + (PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO && + PJSUA_THIRD_PARTY_STREAM_HAS_GET_STAT) */ + /* Print call info */ void print_call(const char *title, @@ -866,11 +888,12 @@ void print_call(const char *title, { int len; pjsip_inv_session *inv = pjsua_var.calls[call_id].inv; - pjsip_dialog *dlg = inv->dlg; + pjsip_dialog *dlg; char userinfo[128]; /* Dump invite sesion info. */ + dlg = (inv? inv->dlg: pjsua_var.calls[call_id].async_call.dlg); len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo)); if (len < 0) pj_ansi_strcpy(userinfo, "<--uri too long-->"); @@ -879,7 +902,8 @@ void print_call(const char *title, len = pj_ansi_snprintf(buf, size, "%s[%s] %s", title, - pjsip_inv_state_name(inv->state), + pjsip_inv_state_name(inv? inv->state: + PJSIP_INV_STATE_DISCONNECTED), userinfo); if (len < 1 || len >= (int)size) { pj_ansi_strcpy(buf, "<--uri too long-->"); diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 8bcb0da..27069a1 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -1,4 +1,4 @@ -/* $Id: pjsua_media.c 4182 2012-06-27 07:12:23Z ming $ */ +/* $Id: pjsua_media.c 4412 2013-03-05 03:12:32Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -165,9 +165,11 @@ pj_status_t pjsua_media_subsys_start(void) #endif /* Perform NAT detection */ - status = pjsua_detect_nat_type(); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, "NAT type detection failed")); + if (pjsua_var.ua_cfg.stun_srv_cnt) { + status = pjsua_detect_nat_type(); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, "NAT type detection failed")); + } } pj_log_pop_indent(); @@ -226,24 +228,33 @@ pj_status_t pjsua_media_subsys_destroy(unsigned flags) * Create RTP and RTCP socket pair, and possibly resolve their public * address via STUN. */ -static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg, +static pj_status_t create_rtp_rtcp_sock(pjsua_call_media *call_med, + const pjsua_transport_config *cfg, pjmedia_sock_info *skinfo) { enum { RTP_RETRY = 100 }; int i; - pj_sockaddr_in bound_addr; - pj_sockaddr_in mapped_addr[2]; + pj_bool_t use_ipv6; + int af; + pj_sockaddr bound_addr; + pj_sockaddr mapped_addr[2]; pj_status_t status = PJ_SUCCESS; - char addr_buf[PJ_INET6_ADDRSTRLEN+2]; + char addr_buf[PJ_INET6_ADDRSTRLEN+10]; pj_sock_t sock[2]; + use_ipv6 = (pjsua_var.acc[call_med->call->acc_id].cfg.ipv6_media_use != + PJSUA_IPV6_DISABLED); + af = use_ipv6 ? pj_AF_INET6() : pj_AF_INET(); + /* Make sure STUN server resolution has completed */ - status = resolve_stun_server(PJ_TRUE); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Error resolving STUN server", status); - return status; + if (!use_ipv6 && pjsua_sip_acc_is_using_stun(call_med->call->acc_id)) { + status = resolve_stun_server(PJ_TRUE); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error resolving STUN server", status); + return status; + } } if (next_rtp_port == 0) @@ -255,9 +266,9 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg, for (i=0; i<2; ++i) sock[i] = PJ_INVALID_SOCKET; - bound_addr.sin_addr.s_addr = PJ_INADDR_ANY; + pj_sockaddr_init(af, &bound_addr, NULL, 0); if (cfg->bound_addr.slen) { - status = pj_sockaddr_in_set_str_addr(&bound_addr, &cfg->bound_addr); + status = pj_sockaddr_set_str_addr(af, &bound_addr, &cfg->bound_addr); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to resolve transport bind address", status); @@ -269,7 +280,7 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg, for (i=0; icall->acc_id) && + pjsua_var.stun_srv.addr.sa_family != 0) + { char ip_addr[32]; pj_str_t stun_srv; + pj_sockaddr_in resolved_addr[2]; + pjstun_setting stun_opt; pj_ansi_strcpy(ip_addr, pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr)); stun_srv = pj_str(ip_addr); - status=pjstun_get_mapped_addr(&pjsua_var.cp.factory, 2, sock, - &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port), - &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port), - mapped_addr); + pj_bzero(&stun_opt, sizeof(stun_opt)); + stun_opt.use_stun2 = pjsua_var.ua_cfg.stun_map_use_stun2; + stun_opt.srv1 = stun_opt.srv2 = stun_srv; + stun_opt.port1 = stun_opt.port2 = + pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port); + status=pjstun_get_mapped_addr2(&pjsua_var.cp.factory, &stun_opt, + 2, sock, resolved_addr); +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 + /* Handle EPIPE (Broken Pipe) error, which happens on UDP socket + * after app wakes up from suspended state. In this case, simply + * just retry. + * P.S.: The magic status is PJ_STATUS_FROM_OS(EPIPE) + */ + if (status == 120032) { + PJ_LOG(4,(THIS_FILE, "Got EPIPE error, retrying..")); + pj_sock_close(sock[0]); + sock[0] = PJ_INVALID_SOCKET; + + pj_sock_close(sock[1]); + sock[1] = PJ_INVALID_SOCKET; + + continue; + } + else +#endif if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "STUN resolve error", status); goto on_error; } + pj_sockaddr_cp(&mapped_addr[0], &resolved_addr[0]); + pj_sockaddr_cp(&mapped_addr[1], &resolved_addr[1]); + #if PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT - if (pj_ntohs(mapped_addr[1].sin_port) == - pj_ntohs(mapped_addr[0].sin_port)+1) + if (pj_sockaddr_get_port(&mapped_addr[1]) == + pj_sockaddr_get_port(&mapped_addr[0])+1) { /* Success! */ break; @@ -349,14 +391,14 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg, pj_sock_close(sock[1]); sock[1] = PJ_INVALID_SOCKET; #else - if (pj_ntohs(mapped_addr[1].sin_port) != - pj_ntohs(mapped_addr[0].sin_port)+1) + if (pj_sockaddr_get_port(&mapped_addr[1]) != + pj_sockaddr_get_port(&mapped_addr[0])+1) { PJ_LOG(4,(THIS_FILE, "Note: STUN mapped RTCP port %d is not adjacent" " to RTP port %d", - pj_ntohs(mapped_addr[1].sin_port), - pj_ntohs(mapped_addr[0].sin_port))); + pj_sockaddr_get_port(&mapped_addr[1]), + pj_sockaddr_get_port(&mapped_addr[0]))); } /* Success! */ break; @@ -364,13 +406,13 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg, } else if (cfg->public_addr.slen) { - status = pj_sockaddr_in_init(&mapped_addr[0], &cfg->public_addr, - (pj_uint16_t)next_rtp_port); + status = pj_sockaddr_init(af, &mapped_addr[0], &cfg->public_addr, + (pj_uint16_t)next_rtp_port); if (status != PJ_SUCCESS) goto on_error; - status = pj_sockaddr_in_init(&mapped_addr[1], &cfg->public_addr, - (pj_uint16_t)(next_rtp_port+1)); + status = pj_sockaddr_init(af, &mapped_addr[1], &cfg->public_addr, + (pj_uint16_t)(next_rtp_port+1)); if (status != PJ_SUCCESS) goto on_error; @@ -378,24 +420,24 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg, } else { - if (bound_addr.sin_addr.s_addr == 0) { + if (!pj_sockaddr_has_addr(&bound_addr)) { pj_sockaddr addr; /* Get local IP address. */ - status = pj_gethostip(pj_AF_INET(), &addr); + status = pj_gethostip(af, &addr); if (status != PJ_SUCCESS) goto on_error; - bound_addr.sin_addr.s_addr = addr.ipv4.sin_addr.s_addr; + pj_sockaddr_copy_addr(&bound_addr, &addr); } for (i=0; i<2; ++i) { - pj_sockaddr_in_init(&mapped_addr[i], NULL, 0); - mapped_addr[i].sin_addr.s_addr = bound_addr.sin_addr.s_addr; + pj_sockaddr_init(af, &mapped_addr[i], NULL, 0); + pj_sockaddr_copy_addr(&mapped_addr[i], &bound_addr); + pj_sockaddr_set_port(&mapped_addr[i], + (pj_uint16_t)(next_rtp_port+i)); } - mapped_addr[0].sin_port=pj_htons((pj_uint16_t)next_rtp_port); - mapped_addr[1].sin_port=pj_htons((pj_uint16_t)(next_rtp_port+1)); break; } } @@ -408,12 +450,10 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg, skinfo->rtp_sock = sock[0]; - pj_memcpy(&skinfo->rtp_addr_name, - &mapped_addr[0], sizeof(pj_sockaddr_in)); + pj_sockaddr_cp(&skinfo->rtp_addr_name, &mapped_addr[0]); skinfo->rtcp_sock = sock[1]; - pj_memcpy(&skinfo->rtcp_addr_name, - &mapped_addr[1], sizeof(pj_sockaddr_in)); + pj_sockaddr_cp(&skinfo->rtcp_addr_name, &mapped_addr[1]); PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s", pj_sockaddr_print(&skinfo->rtp_addr_name, addr_buf, @@ -440,7 +480,7 @@ static pj_status_t create_udp_media_transport(const pjsua_transport_config *cfg, pjmedia_sock_info skinfo; pj_status_t status; - status = create_rtp_rtcp_sock(cfg, &skinfo); + status = create_rtp_rtcp_sock(call_med, cfg, &skinfo); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create RTP/RTCP socket", status); @@ -512,21 +552,59 @@ on_error: } #endif -static void med_tp_timer_cb(void *user_data) +/* Deferred callback to notify ICE init complete */ +static void ice_init_complete_cb(void *user_data) { pjsua_call_media *call_med = (pjsua_call_media*)user_data; - pjsua_call *call = NULL; - pjsip_dialog *dlg = NULL; - acquire_call("med_tp_timer_cb", call_med->call->index, &call, &dlg); + if (call_med->call == NULL) + return; + /* No need to acquire_call() if we only change the tp_ready flag + * (i.e. transport is being created synchronously). Otherwise + * calling acquire_call() here may cause deadlock. See + * https://trac.pjsip.org/repos/ticket/1578 + */ call_med->tp_ready = call_med->tp_result; - if (call_med->med_create_cb) + + if (call_med->med_create_cb) { + pjsua_call *call = NULL; + pjsip_dialog *dlg = NULL; + + if (acquire_call("ice_init_complete_cb", call_med->call->index, + &call, &dlg) != PJ_SUCCESS) + { + /* Call have been terminated */ + return; + } + (*call_med->med_create_cb)(call_med, call_med->tp_ready, call_med->call->secure_level, NULL); + if (dlg) + pjsip_dlg_dec_lock(dlg); + } +} + +/* Deferred callback to notify ICE negotiation failure */ +static void ice_failed_nego_cb(void *user_data) +{ + int call_id = (int)(long)user_data; + pjsua_call *call = NULL; + pjsip_dialog *dlg = NULL; + + if (acquire_call("ice_failed_nego_cb", call_id, + &call, &dlg) != PJ_SUCCESS) + { + /* Call have been terminated */ + return; + } + + pjsua_var.ua_cfg.cb.on_call_media_state(call_id); + if (dlg) - pjsip_dlg_dec_lock(dlg); + pjsip_dlg_dec_lock(dlg); + } /* This callback is called when ICE negotiation completes */ @@ -535,73 +613,43 @@ static void on_ice_complete(pjmedia_transport *tp, pj_status_t result) { pjsua_call_media *call_med = (pjsua_call_media*)tp->user_data; + pjsua_call *call; if (!call_med) return; + call = call_med->call; + switch (op) { case PJ_ICE_STRANS_OP_INIT: call_med->tp_result = result; - pjsua_schedule_timer2(&med_tp_timer_cb, call_med, 1); + pjsua_schedule_timer2(&ice_init_complete_cb, call_med, 1); break; case PJ_ICE_STRANS_OP_NEGOTIATION: - if (result != PJ_SUCCESS) { + if (result == PJ_SUCCESS) { + /* Update RTP address */ + pjmedia_transport_info tpinfo; + pjmedia_transport_info_init(&tpinfo); + pjmedia_transport_get_info(call_med->tp, &tpinfo); + pj_sockaddr_cp(&call_med->rtp_addr, &tpinfo.sock_info.rtp_addr_name); + } else { call_med->state = PJSUA_CALL_MEDIA_ERROR; call_med->dir = PJMEDIA_DIR_NONE; - - if (call_med->call && pjsua_var.ua_cfg.cb.on_call_media_state) { - pjsua_var.ua_cfg.cb.on_call_media_state(call_med->call->index); - } - } else if (call_med->call) { - /* Send UPDATE if default transport address is different than - * what was advertised (ticket #881) - */ - pjmedia_transport_info tpinfo; - pjmedia_ice_transport_info *ii = NULL; - unsigned i; - - pjmedia_transport_info_init(&tpinfo); - pjmedia_transport_get_info(tp, &tpinfo); - for (i=0; irole==PJ_ICE_SESS_ROLE_CONTROLLING && - pj_sockaddr_cmp(&tpinfo.sock_info.rtp_addr_name, - &call_med->rtp_addr)) - { - pj_bool_t use_update; - const pj_str_t STR_UPDATE = { "UPDATE", 6 }; - pjsip_dialog_cap_status support_update; - pjsip_dialog *dlg; - - dlg = call_med->call->inv->dlg; - support_update = pjsip_dlg_remote_has_cap(dlg, PJSIP_H_ALLOW, - NULL, &STR_UPDATE); - use_update = (support_update == PJSIP_DIALOG_CAP_SUPPORTED); - - PJ_LOG(4,(THIS_FILE, - "ICE default transport address has changed for " - "call %d, sending %s", - call_med->call->index, - (use_update ? "UPDATE" : "re-INVITE"))); - - if (use_update) - pjsua_call_update(call_med->call->index, 0, NULL); - else - pjsua_call_reinvite(call_med->call->index, 0, NULL); + if (call && pjsua_var.ua_cfg.cb.on_call_media_state) { + /* Defer the callback to a timer */ + pjsua_schedule_timer2(&ice_failed_nego_cb, + (void*)(long)call->index, 1); } - } + } + /* Check if default ICE transport address is changed */ + call->reinv_ice_sent = PJ_FALSE; + pjsua_call_schedule_reinvite_check(call, 0); break; case PJ_ICE_STRANS_OP_KEEP_ALIVE: if (result != PJ_SUCCESS) { PJ_PERROR(4,(THIS_FILE, result, "ICE keep alive failure for transport %d:%d", - call_med->call->index, call_med->idx)); + call->index, call_med->idx)); } if (pjsua_var.ua_cfg.cb.on_call_media_transport_state) { pjsua_med_tp_state_info info; @@ -612,10 +660,10 @@ static void on_ice_complete(pjmedia_transport *tp, info.status = result; info.ext_info = &op; (*pjsua_var.ua_cfg.cb.on_call_media_transport_state)( - call_med->call->index, &info); + call->index, &info); } if (pjsua_var.ua_cfg.cb.on_ice_transport_error) { - pjsua_call_id id = call_med->call->index; + pjsua_call_id id = call->index; (*pjsua_var.ua_cfg.cb.on_ice_transport_error)(id, op, result, NULL); } @@ -657,12 +705,15 @@ static pj_status_t create_ice_media_transport( pj_bool_t async) { char stunip[PJ_INET6_ADDRSTRLEN]; + pjsua_acc_config *acc_cfg; pj_ice_strans_cfg ice_cfg; pjmedia_ice_cb ice_cb; char name[32]; unsigned comp_cnt; pj_status_t status; + acc_cfg = &pjsua_var.acc[call_med->call->acc_id].cfg; + /* Make sure STUN server resolution has completed */ status = resolve_stun_server(PJ_TRUE); if (status != PJ_SUCCESS) { @@ -679,7 +730,7 @@ static pj_status_t create_ice_media_transport( ice_cfg.af = pj_AF_INET(); ice_cfg.resolver = pjsua_var.resolver; - ice_cfg.opt = pjsua_var.media_cfg.ice_opt; + ice_cfg.opt = acc_cfg->ice_cfg.ice_opt; /* Configure STUN settings */ if (pj_sockaddr_has_addr(&pjsua_var.stun_srv)) { @@ -687,8 +738,16 @@ static pj_status_t create_ice_media_transport( ice_cfg.stun.server = pj_str(stunip); ice_cfg.stun.port = pj_sockaddr_get_port(&pjsua_var.stun_srv); } - if (pjsua_var.media_cfg.ice_max_host_cands >= 0) - ice_cfg.stun.max_host_cands = pjsua_var.media_cfg.ice_max_host_cands; + if (acc_cfg->ice_cfg.ice_max_host_cands >= 0) + ice_cfg.stun.max_host_cands = acc_cfg->ice_cfg.ice_max_host_cands; + + /* Copy binding port setting to STUN setting */ + pj_sockaddr_init(ice_cfg.af, &ice_cfg.stun.cfg.bound_addr, + &cfg->bound_addr, (pj_uint16_t)cfg->port); + ice_cfg.stun.cfg.port_range = (pj_uint16_t)cfg->port_range; + if (cfg->port != 0 && ice_cfg.stun.cfg.port_range == 0) + ice_cfg.stun.cfg.port_range = + (pj_uint16_t)(pjsua_var.ua_cfg.max_calls * 10); /* Copy QoS setting to STUN setting */ ice_cfg.stun.cfg.qos_type = cfg->qos_type; @@ -696,8 +755,8 @@ static pj_status_t create_ice_media_transport( sizeof(cfg->qos_params)); /* Configure TURN settings */ - if (pjsua_var.media_cfg.enable_turn) { - status = parse_host_port(&pjsua_var.media_cfg.turn_server, + if (acc_cfg->turn_cfg.enable_turn) { + status = parse_host_port(&acc_cfg->turn_cfg.turn_server, &ice_cfg.turn.server, &ice_cfg.turn.port); if (status != PJ_SUCCESS || ice_cfg.turn.server.slen == 0) { @@ -706,24 +765,36 @@ static pj_status_t create_ice_media_transport( } if (ice_cfg.turn.port == 0) ice_cfg.turn.port = 3479; - ice_cfg.turn.conn_type = pjsua_var.media_cfg.turn_conn_type; + ice_cfg.turn.conn_type = acc_cfg->turn_cfg.turn_conn_type; pj_memcpy(&ice_cfg.turn.auth_cred, - &pjsua_var.media_cfg.turn_auth_cred, + &acc_cfg->turn_cfg.turn_auth_cred, sizeof(ice_cfg.turn.auth_cred)); /* Copy QoS setting to TURN setting */ ice_cfg.turn.cfg.qos_type = cfg->qos_type; pj_memcpy(&ice_cfg.turn.cfg.qos_params, &cfg->qos_params, sizeof(cfg->qos_params)); + + /* Copy binding port setting to TURN setting */ + pj_sockaddr_init(ice_cfg.af, &ice_cfg.turn.cfg.bound_addr, + &cfg->bound_addr, (pj_uint16_t)cfg->port); + ice_cfg.turn.cfg.port_range = (pj_uint16_t)cfg->port_range; + if (cfg->port != 0 && ice_cfg.turn.cfg.port_range == 0) + ice_cfg.turn.cfg.port_range = + (pj_uint16_t)(pjsua_var.ua_cfg.max_calls * 10); } + /* Configure packet size for STUN and TURN sockets */ + ice_cfg.stun.cfg.max_pkt_size = PJMEDIA_MAX_MRU; + ice_cfg.turn.cfg.max_pkt_size = PJMEDIA_MAX_MRU; + pj_bzero(&ice_cb, sizeof(pjmedia_ice_cb)); ice_cb.on_ice_complete = &on_ice_complete; pj_ansi_snprintf(name, sizeof(name), "icetp%02d", call_med->idx); call_med->tp_ready = PJ_EPENDING; comp_cnt = 1; - if (PJMEDIA_ADVERTISE_RTCP && !pjsua_var.media_cfg.ice_no_rtcp) + if (PJMEDIA_ADVERTISE_RTCP && !acc_cfg->ice_cfg.ice_no_rtcp) ++comp_cnt; status = pjmedia_ice_create3(pjsua_var.med_endpt, name, comp_cnt, @@ -761,7 +832,7 @@ static pj_status_t create_ice_media_transport( pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING, pjsua_var.media_cfg.rx_drop_pct); - + return PJ_SUCCESS; on_error: @@ -851,7 +922,7 @@ PJ_DEF(pj_status_t) pjsua_media_transports_create( pjsua_transport_config_dup(pjsua_var.pool, &cfg, app_cfg); /* Create the transports */ - if (pjsua_var.media_cfg.enable_ice) { + if (pjsua_var.ice_cfg.enable_ice) { status = create_ice_media_transports(&cfg); } else { status = create_udp_media_transports(&cfg); @@ -1237,7 +1308,7 @@ pj_status_t pjsua_call_media_init(pjsua_call_media *call_med, pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_CREATING); - if (pjsua_var.media_cfg.enable_ice) { + if (pjsua_var.acc[call_med->call->acc_id].cfg.ice_cfg.enable_ice) { status = create_ice_media_transport(tcfg, call_med, async); if (async && status == PJ_EPENDING) { /* We will resume call media initialization in the @@ -1245,7 +1316,7 @@ pj_status_t pjsua_call_media_init(pjsua_call_media *call_med, */ call_med->med_create_cb = &call_media_init_cb; call_med->med_init_cb = cb; - + return PJ_EPENDING; } } else { @@ -1344,7 +1415,7 @@ static pj_status_t media_channel_init_cb(pjsua_call_id call_id, } if (call_med->use_custom_med_tp) { - unsigned custom_med_tp_flags = 0; + unsigned custom_med_tp_flags = PJSUA_MED_TP_CLOSE_MEMBER; /* Use custom media transport returned by the application */ call_med->tp = @@ -1388,12 +1459,12 @@ on_return: /* Clean up media transports in provisional media that is not used * by call media. */ -void pjsua_media_prov_clean_up(pjsua_call_id call_id) +static void media_prov_clean_up(pjsua_call_id call_id, int idx) { pjsua_call *call = &pjsua_var.calls[call_id]; unsigned i; - for (i = 0; i < call->med_prov_cnt; ++i) { + for (i = idx; i < call->med_prov_cnt; ++i) { pjsua_call_media *call_med = &call->media_prov[i]; unsigned j; pj_bool_t used = PJ_FALSE; @@ -1420,6 +1491,11 @@ void pjsua_media_prov_clean_up(pjsua_call_id call_id) } } +void pjsua_media_prov_clean_up(pjsua_call_id call_id) +{ + media_prov_clean_up(call_id, 0); +} + pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, pjsip_role_e role, @@ -1452,8 +1528,10 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, * (e.g. in reinvites, updates, etc). */ - if (pjsua_get_state() != PJSUA_STATE_RUNNING) + if (pjsua_get_state() != PJSUA_STATE_RUNNING) { + if (sip_err_code) *sip_err_code = PJSIP_SC_SERVICE_UNAVAILABLE; return PJ_EBUSY; + } if (async) { pj_pool_t *tmppool = (call->inv? call->inv->pool_prov: @@ -1831,6 +1909,8 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, if (rem_sdp && mi >= rem_sdp->media_count) { /* Remote might have removed some media lines. */ + media_prov_clean_up(call->index, rem_sdp->media_count); + call->med_prov_cnt = rem_sdp->media_count; break; } @@ -1848,10 +1928,6 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media); m->desc.transport = pj_str("RTP/AVP"); m->desc.fmt_count = 1; - m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn); - m->conn->net_type = pj_str("IN"); - m->conn->addr_type = pj_str("IP4"); - m->conn->addr = pj_str("127.0.0.1"); switch (call_med->type) { case PJMEDIA_TYPE_AUDIO: @@ -1881,6 +1957,24 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, } } + /* Add connection line, if none */ + if (m->conn == NULL && sdp->conn == NULL) { + pj_bool_t use_ipv6; + + use_ipv6 = (pjsua_var.acc[call->acc_id].cfg.ipv6_media_use != + PJSUA_IPV6_DISABLED); + + m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn); + m->conn->net_type = pj_str("IN"); + if (use_ipv6) { + m->conn->addr_type = pj_str("IP6"); + m->conn->addr = pj_str("::1"); + } else { + m->conn->addr_type = pj_str("IP4"); + m->conn->addr = pj_str("127.0.0.1"); + } + } + sdp->media[sdp->media_count++] = m; continue; } @@ -1919,12 +2013,14 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, return status; } +#if PJSUA_SDP_SESS_HAS_CONN /* Copy c= line of the first media to session level, * if there's none. */ if (sdp->conn == NULL) { sdp->conn = pjmedia_sdp_conn_clone(pool, m->conn); } +#endif /* Find media bandwidth info */ @@ -2049,63 +2145,73 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, } -static void stop_media_session(pjsua_call_id call_id) +static void stop_media_stream(pjsua_call *call, unsigned med_idx) { - pjsua_call *call = &pjsua_var.calls[call_id]; - unsigned mi; + pjsua_call_media *call_med = &call->media[med_idx]; - pj_log_push_indent(); + /* Check if stream does not exist */ + if (med_idx >= call->med_cnt) + return; - for (mi=0; mimed_cnt; ++mi) { - pjsua_call_media *call_med = &call->media[mi]; + pj_log_push_indent(); - if (call_med->type == PJMEDIA_TYPE_AUDIO) { - pjsua_aud_stop_stream(call_med); - } + if (call_med->type == PJMEDIA_TYPE_AUDIO) { + pjsua_aud_stop_stream(call_med); + } #if PJMEDIA_HAS_VIDEO - else if (call_med->type == PJMEDIA_TYPE_VIDEO) { - pjsua_vid_stop_stream(call_med); - } + else if (call_med->type == PJMEDIA_TYPE_VIDEO) { + pjsua_vid_stop_stream(call_med); + } #endif - PJ_LOG(4,(THIS_FILE, "Media session call%02d:%d is destroyed", - call_id, mi)); - call_med->prev_state = call_med->state; - call_med->state = PJSUA_CALL_MEDIA_NONE; + PJ_LOG(4,(THIS_FILE, "Media stream call%02d:%d is destroyed", + call->index, med_idx)); + call_med->prev_state = call_med->state; + call_med->state = PJSUA_CALL_MEDIA_NONE; - /* Try to sync recent changes to provisional media */ - if (mimed_prov_cnt && call->media_prov[mi].tp==call_med->tp) - { - pjsua_call_media *prov_med = &call->media_prov[mi]; + /* Try to sync recent changes to provisional media */ + if (med_idx < call->med_prov_cnt && + call->media_prov[med_idx].tp == call_med->tp) + { + pjsua_call_media *prov_med = &call->media_prov[med_idx]; - /* Media state */ - prov_med->prev_state = call_med->prev_state; - prov_med->state = call_med->state; + /* Media state */ + prov_med->prev_state = call_med->prev_state; + prov_med->state = call_med->state; - /* RTP seq/ts */ - prov_med->rtp_tx_seq_ts_set = call_med->rtp_tx_seq_ts_set; - prov_med->rtp_tx_seq = call_med->rtp_tx_seq; - prov_med->rtp_tx_ts = call_med->rtp_tx_ts; + /* RTP seq/ts */ + prov_med->rtp_tx_seq_ts_set = call_med->rtp_tx_seq_ts_set; + prov_med->rtp_tx_seq = call_med->rtp_tx_seq; + prov_med->rtp_tx_ts = call_med->rtp_tx_ts; - /* Stream */ - if (call_med->type == PJMEDIA_TYPE_AUDIO) { - prov_med->strm.a.conf_slot = call_med->strm.a.conf_slot; - prov_med->strm.a.stream = call_med->strm.a.stream; - } + /* Stream */ + if (call_med->type == PJMEDIA_TYPE_AUDIO) { + prov_med->strm.a.conf_slot = call_med->strm.a.conf_slot; + prov_med->strm.a.stream = call_med->strm.a.stream; + } #if PJMEDIA_HAS_VIDEO - else if (call_med->type == PJMEDIA_TYPE_VIDEO) { - prov_med->strm.v.cap_win_id = call_med->strm.v.cap_win_id; - prov_med->strm.v.rdr_win_id = call_med->strm.v.rdr_win_id; - prov_med->strm.v.stream = call_med->strm.v.stream; - } -#endif + else if (call_med->type == PJMEDIA_TYPE_VIDEO) { + prov_med->strm.v.cap_win_id = call_med->strm.v.cap_win_id; + prov_med->strm.v.rdr_win_id = call_med->strm.v.rdr_win_id; + prov_med->strm.v.stream = call_med->strm.v.stream; } +#endif } pj_log_pop_indent(); } +static void stop_media_session(pjsua_call_id call_id) +{ + pjsua_call *call = &pjsua_var.calls[call_id]; + unsigned mi; + + for (mi=0; mimed_cnt; ++mi) { + stop_media_stream(call, mi); + } +} + pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id) { pjsua_call *call = &pjsua_var.calls[call_id]; @@ -2153,6 +2259,197 @@ pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id) } +/* Match codec fmtp. This will compare the values and the order. */ +static pj_bool_t match_codec_fmtp(const pjmedia_codec_fmtp *fmtp1, + const pjmedia_codec_fmtp *fmtp2) +{ + unsigned i; + + if (fmtp1->cnt != fmtp2->cnt) + return PJ_FALSE; + + for (i = 0; i < fmtp1->cnt; ++i) { + if (pj_stricmp(&fmtp1->param[i].name, &fmtp2->param[i].name)) + return PJ_FALSE; + if (pj_stricmp(&fmtp1->param[i].val, &fmtp2->param[i].val)) + return PJ_FALSE; + } + + return PJ_TRUE; +} + +#if PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO + +static pj_bool_t is_ice_running(pjmedia_transport *tp) +{ + pjmedia_transport_info tpinfo; + pjmedia_ice_transport_info *ice_info; + + pjmedia_transport_info_init(&tpinfo); + pjmedia_transport_get_info(tp, &tpinfo); + ice_info = (pjmedia_ice_transport_info*) + pjmedia_transport_info_get_spc_info(&tpinfo, + PJMEDIA_TRANSPORT_TYPE_ICE); + return (ice_info && ice_info->sess_state == PJ_ICE_STRANS_STATE_RUNNING); +} + + +static pj_bool_t is_media_changed(const pjsua_call *call, + unsigned med_idx, + const pjsua_stream_info *new_si_) +{ + const pjsua_call_media *call_med = &call->media[med_idx]; + + /* Check for newly added media */ + if (med_idx >= call->med_cnt) + return PJ_TRUE; + + /* Compare media type */ + if (call_med->type != new_si_->type) + return PJ_TRUE; + + /* Audio update checks */ + if (call_med->type == PJMEDIA_TYPE_AUDIO) { + pjmedia_stream_info the_old_si; + const pjmedia_stream_info *old_si = NULL; + const pjmedia_stream_info *new_si = &new_si_->info.aud; + const pjmedia_codec_info *old_ci = NULL; + const pjmedia_codec_info *new_ci = &new_si->fmt; + const pjmedia_codec_param *old_cp = NULL; + const pjmedia_codec_param *new_cp = new_si->param; + + /* Compare media direction */ + if (call_med->dir != new_si->dir) + return PJ_TRUE; + + /* Get current active stream info */ + if (call_med->strm.a.stream) { + pjmedia_stream_get_info(call_med->strm.a.stream, &the_old_si); + old_si = &the_old_si; + old_ci = &old_si->fmt; + old_cp = old_si->param; + } else { + /* The stream is inactive. */ + return (new_si->dir != PJMEDIA_DIR_NONE); + } + + /* Compare remote RTP address. If ICE is running, change in default + * address can happen after negotiation, this can be handled + * internally by ICE and does not need to cause media restart. + */ + if (!is_ice_running(call_med->tp) && + pj_sockaddr_cmp(&old_si->rem_addr, &new_si->rem_addr)) + { + return PJ_TRUE; + } + + /* Compare codec info */ + if (pj_stricmp(&old_ci->encoding_name, &new_ci->encoding_name) || + old_ci->clock_rate != new_ci->clock_rate || + old_ci->channel_cnt != new_ci->channel_cnt || + old_si->rx_pt != new_si->rx_pt || + old_si->tx_pt != new_si->tx_pt || + old_si->rx_event_pt != new_si->tx_event_pt || + old_si->tx_event_pt != new_si->tx_event_pt) + { + return PJ_TRUE; + } + + /* Compare codec param */ + if (old_cp->setting.frm_per_pkt != new_cp->setting.frm_per_pkt || + old_cp->setting.vad != new_cp->setting.vad || + old_cp->setting.cng != new_cp->setting.cng || + old_cp->setting.plc != new_cp->setting.plc || + old_cp->setting.penh != new_cp->setting.penh || + !match_codec_fmtp(&old_cp->setting.dec_fmtp, + &new_cp->setting.dec_fmtp) || + !match_codec_fmtp(&old_cp->setting.enc_fmtp, + &new_cp->setting.enc_fmtp)) + { + return PJ_TRUE; + } + } + +#if PJMEDIA_HAS_VIDEO + else if (call_med->type == PJMEDIA_TYPE_VIDEO) { + pjmedia_vid_stream_info the_old_si; + const pjmedia_vid_stream_info *old_si = NULL; + const pjmedia_vid_stream_info *new_si = &new_si_->info.vid; + const pjmedia_vid_codec_info *old_ci = NULL; + const pjmedia_vid_codec_info *new_ci = &new_si->codec_info; + const pjmedia_vid_codec_param *old_cp = NULL; + const pjmedia_vid_codec_param *new_cp = new_si->codec_param; + + /* Compare media direction */ + if (call_med->dir != new_si->dir) + return PJ_TRUE; + + /* Get current active stream info */ + if (call_med->strm.v.stream) { + pjmedia_vid_stream_get_info(call_med->strm.v.stream, &the_old_si); + old_si = &the_old_si; + old_ci = &old_si->codec_info; + old_cp = old_si->codec_param; + } else { + /* The stream is inactive. */ + return (new_si->dir != PJMEDIA_DIR_NONE); + } + + /* Compare remote RTP address. If ICE is running, change in default + * address can happen after negotiation, this can be handled + * internally by ICE and does not need to cause media restart. + */ + if (!is_ice_running(call_med->tp) && + pj_sockaddr_cmp(&old_si->rem_addr, &new_si->rem_addr)) + { + return PJ_TRUE; + } + + /* Compare codec info */ + if (pj_stricmp(&old_ci->encoding_name, &new_ci->encoding_name) || + old_si->rx_pt != new_si->rx_pt || + old_si->tx_pt != new_si->tx_pt) + { + return PJ_TRUE; + } + + /* Compare codec param */ + if (/* old_cp->enc_mtu != new_cp->enc_mtu || */ + pj_memcmp(&old_cp->enc_fmt.det, &new_cp->enc_fmt.det, + sizeof(pjmedia_video_format_detail)) || + !match_codec_fmtp(&old_cp->dec_fmtp, &new_cp->dec_fmtp) || + !match_codec_fmtp(&old_cp->enc_fmtp, &new_cp->enc_fmtp)) + { + return PJ_TRUE; + } + } + +#endif + + else { + /* Just return PJ_TRUE for other media type */ + return PJ_TRUE; + } + + return PJ_FALSE; +} + +#else /* PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO */ + +static pj_bool_t is_media_changed(const pjsua_call *call, + unsigned med_idx, + const pjsua_stream_info *new_si_) +{ + PJ_UNUSED_ARG(call); + PJ_UNUSED_ARG(med_idx); + PJ_UNUSED_ARG(new_si_); + /* Always assume that media has been changed */ + return PJ_TRUE; +} + +#endif /* PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO */ + + pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, const pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *remote_sdp) @@ -2181,7 +2478,7 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, pj_log_push_indent(); /* Destroy existing media session, if any. */ - stop_media_session(call->index); + //stop_media_session(call->index); /* Call media count must be at least equal to SDP media. Note that * it may not be equal when remote removed any SDP media line. @@ -2235,6 +2532,7 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, /* Process each media stream */ for (mi=0; mi < call->med_prov_cnt; ++mi) { pjsua_call_media *call_med = &call->media_prov[mi]; + pj_bool_t media_changed = PJ_FALSE; if (mi >= local_sdp->media_count || mi >= remote_sdp->media_count) @@ -2242,8 +2540,12 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, /* This may happen when remote removed any SDP media lines in * its re-offer. */ + + /* Stop stream */ + stop_media_stream(call, mi); + + /* Close the media transport */ if (call_med->tp) { - /* Close the media transport */ pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL); pjmedia_transport_close(call_med->tp); call_med->tp = call_med->tp_orig = NULL; @@ -2258,11 +2560,14 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, #endif } + /* Apply media update action */ if (call_med->type==PJMEDIA_TYPE_AUDIO) { pjmedia_stream_info the_si, *si = &the_si; + pjsua_stream_info stream_info; - status = pjmedia_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt, - local_sdp, remote_sdp, mi); + status = pjmedia_stream_info_from_sdp( + si, tmp_pool, pjsua_var.med_endpt, + local_sdp, remote_sdp, mi); if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "pjmedia_stream_info_from_sdp() failed " @@ -2271,14 +2576,43 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, continue; } + /* Override ptime, if this option is specified. */ + if (pjsua_var.media_cfg.ptime != 0) { + si->param->setting.frm_per_pkt = (pj_uint8_t) + (pjsua_var.media_cfg.ptime / si->param->info.frm_ptime); + if (si->param->setting.frm_per_pkt == 0) + si->param->setting.frm_per_pkt = 1; + } + + /* Disable VAD, if this option is specified. */ + if (pjsua_var.media_cfg.no_vad) { + si->param->setting.vad = 0; + } + + /* Check if this media is changed */ + stream_info.type = PJMEDIA_TYPE_AUDIO; + stream_info.info.aud = the_si; + if (pjsua_var.media_cfg.no_smart_media_update || + is_media_changed(call, mi, &stream_info)) + { + media_changed = PJ_TRUE; + /* Stop the media */ + stop_media_stream(call, mi); + } else { + PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (audio) unchanged.", + call_id, mi)); + } + /* Check if no media is active */ if (si->dir == PJMEDIA_DIR_NONE) { + /* Update call media state and direction */ call_med->state = PJSUA_CALL_MEDIA_NONE; call_med->dir = PJMEDIA_DIR_NONE; } else { pjmedia_transport_info tp_info; + pjmedia_srtp_info *srtp_info; /* Start/restart media transport based on info in SDP */ status = pjmedia_transport_media_start(call_med->tp, @@ -2297,17 +2631,24 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, /* Get remote SRTP usage policy */ pjmedia_transport_info_init(&tp_info); pjmedia_transport_get_info(call_med->tp, &tp_info); - if (tp_info.specific_info_cnt > 0) { - unsigned i; - for (i = 0; i < tp_info.specific_info_cnt; ++i) { - if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP) - { - pjmedia_srtp_info *srtp_info = - (pjmedia_srtp_info*) tp_info.spc_info[i].buffer; - - call_med->rem_srtp_use = srtp_info->peer_use; - break; - } + srtp_info = (pjmedia_srtp_info*) + pjmedia_transport_info_get_spc_info( + &tp_info, PJMEDIA_TRANSPORT_TYPE_SRTP); + if (srtp_info) { + call_med->rem_srtp_use = srtp_info->peer_use; + } + + /* Update audio channel */ + if (media_changed) { + status = pjsua_aud_channel_update(call_med, + call->inv->pool, si, + local_sdp, remote_sdp); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "pjsua_aud_channel_update() failed " + "for call_id %d media %d", + call_id, mi)); + continue; } } @@ -2323,17 +2664,6 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, call_med->state = PJSUA_CALL_MEDIA_ACTIVE; } - /* Call implementation */ - status = pjsua_aud_channel_update(call_med, tmp_pool, si, - local_sdp, remote_sdp); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjsua_aud_channel_update() failed " - "for call_id %d media %d", - call_id, mi)); - continue; - } - /* Print info. */ if (status == PJ_SUCCESS) { char info[80]; @@ -2378,9 +2708,11 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) } else if (call_med->type==PJMEDIA_TYPE_VIDEO) { pjmedia_vid_stream_info the_si, *si = &the_si; + pjsua_stream_info stream_info; - status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt, - local_sdp, remote_sdp, mi); + status = pjmedia_vid_stream_info_from_sdp( + si, tmp_pool, pjsua_var.med_endpt, + local_sdp, remote_sdp, mi); if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "pjmedia_vid_stream_info_from_sdp() failed " @@ -2389,14 +2721,28 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, continue; } + /* Check if this media is changed */ + stream_info.type = PJMEDIA_TYPE_VIDEO; + stream_info.info.vid = the_si; + if (is_media_changed(call, mi, &stream_info)) { + media_changed = PJ_TRUE; + /* Stop the media */ + stop_media_stream(call, mi); + } else { + PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (video) unchanged.", + call_id, mi)); + } + /* Check if no media is active */ if (si->dir == PJMEDIA_DIR_NONE) { + /* Update call media state and direction */ call_med->state = PJSUA_CALL_MEDIA_NONE; call_med->dir = PJMEDIA_DIR_NONE; } else { pjmedia_transport_info tp_info; + pjmedia_srtp_info *srtp_info; /* Start/restart media transport */ status = pjmedia_transport_media_start(call_med->tp, @@ -2415,17 +2761,24 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, /* Get remote SRTP usage policy */ pjmedia_transport_info_init(&tp_info); pjmedia_transport_get_info(call_med->tp, &tp_info); - if (tp_info.specific_info_cnt > 0) { - unsigned i; - for (i = 0; i < tp_info.specific_info_cnt; ++i) { - if (tp_info.spc_info[i].type == - PJMEDIA_TRANSPORT_TYPE_SRTP) - { - pjmedia_srtp_info *sri; - sri=(pjmedia_srtp_info*)tp_info.spc_info[i].buffer; - call_med->rem_srtp_use = sri->peer_use; - break; - } + srtp_info = (pjmedia_srtp_info*) + pjmedia_transport_info_get_spc_info( + &tp_info, PJMEDIA_TRANSPORT_TYPE_SRTP); + if (srtp_info) { + call_med->rem_srtp_use = srtp_info->peer_use; + } + + /* Update audio channel */ + if (media_changed) { + status = pjsua_vid_channel_update(call_med, + call->inv->pool, si, + local_sdp, remote_sdp); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "pjsua_vid_channel_update() failed " + "for call_id %d media %d", + call_id, mi)); + continue; } } @@ -2441,16 +2794,6 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, call_med->state = PJSUA_CALL_MEDIA_ACTIVE; } - status = pjsua_vid_channel_update(call_med, tmp_pool, si, - local_sdp, remote_sdp); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjsua_vid_channel_update() failed " - "for call_id %d media %d", - call_id, mi)); - continue; - } - /* Print info. */ { char info[80]; diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c index 4551c91..591f65b 100644 --- a/pjsip/src/pjsua-lib/pjsua_pres.c +++ b/pjsip/src/pjsua-lib/pjsua_pres.c @@ -1,4 +1,4 @@ -/* $Id: pjsua_pres.c 4186 2012-06-29 01:37:50Z ming $ */ +/* $Id: pjsua_pres.c 4294 2012-11-06 05:02:10Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -864,8 +864,31 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata) return PJ_TRUE; } - if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) + if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) { pjsip_dlg_set_via_sent_by(dlg, &acc->via_addr, acc->via_tp); + } else if (!pjsua_sip_acc_is_using_stun(acc_id)) { + /* Choose local interface to use in Via if acc is not using + * STUN. See https://trac.pjsip.org/repos/ticket/1412 + */ + char target_buf[PJSIP_MAX_URL_SIZE]; + pj_str_t target; + pjsip_host_port via_addr; + const void *via_tp; + + target.ptr = target_buf; + target.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, + dlg->target, + target_buf, sizeof(target_buf)); + if (target.slen < 0) target.slen = 0; + + if (pjsua_acc_get_uac_addr(acc_id, dlg->pool, &target, + &via_addr, NULL, NULL, + &via_tp) == PJ_SUCCESS) + { + pjsip_dlg_set_via_sent_by(dlg, &via_addr, + (pjsip_transport*)via_tp); + } + } /* Set credentials and preference. */ pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred); @@ -1236,6 +1259,20 @@ static pj_status_t send_publish(int acc_id, pj_bool_t active) if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) { pjsip_publishc_set_via_sent_by(acc->publish_sess, &acc->via_addr, acc->via_tp); + } else if (!pjsua_sip_acc_is_using_stun(acc_id)) { + /* Choose local interface to use in Via if acc is not using + * STUN. See https://trac.pjsip.org/repos/ticket/1412 + */ + pjsip_host_port via_addr; + const void *via_tp; + + if (pjsua_acc_get_uac_addr(acc_id, acc->pool, &acc_cfg->id, + &via_addr, NULL, NULL, + &via_tp) == PJ_SUCCESS) + { + pjsip_publishc_set_via_sent_by(acc->publish_sess, &via_addr, + (pjsip_transport*)via_tp); + } } /* Send the PUBLISH request */ @@ -1789,8 +1826,24 @@ static void subscribe_buddy_presence(pjsua_buddy_id buddy_id) */ pjsip_dlg_inc_lock(buddy->dlg); - if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) + if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) { pjsip_dlg_set_via_sent_by(buddy->dlg, &acc->via_addr, acc->via_tp); + } else if (!pjsua_sip_acc_is_using_stun(acc_id)) { + /* Choose local interface to use in Via if acc is not using + * STUN. See https://trac.pjsip.org/repos/ticket/1412 + */ + pjsip_host_port via_addr; + const void *via_tp; + + if (pjsua_acc_get_uac_addr(acc_id, buddy->dlg->pool, &buddy->uri, + &via_addr, NULL, NULL, + &via_tp) == PJ_SUCCESS) + { + pjsip_dlg_set_via_sent_by(buddy->dlg, &via_addr, + (pjsip_transport*)via_tp); + } + } + status = pjsip_pres_create_uac( buddy->dlg, &pres_callback, PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub); @@ -2114,8 +2167,23 @@ pj_status_t pjsua_start_mwi(pjsua_acc_id acc_id, pj_bool_t force_renew) */ pjsip_dlg_inc_lock(acc->mwi_dlg); - if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) + if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) { pjsip_dlg_set_via_sent_by(acc->mwi_dlg, &acc->via_addr, acc->via_tp); + } else if (!pjsua_sip_acc_is_using_stun(acc_id)) { + /* Choose local interface to use in Via if acc is not using + * STUN. See https://trac.pjsip.org/repos/ticket/1412 + */ + pjsip_host_port via_addr; + const void *via_tp; + + if (pjsua_acc_get_uac_addr(acc_id, acc->mwi_dlg->pool, &acc->cfg.id, + &via_addr, NULL, NULL, + &via_tp) == PJ_SUCCESS) + { + pjsip_dlg_set_via_sent_by(acc->mwi_dlg, &via_addr, + (pjsip_transport*)via_tp); + } + } /* Create UAC subscription */ status = pjsip_mwi_create_uac(acc->mwi_dlg, &mwi_cb, diff --git a/pjsip/src/pjsua-lib/pjsua_vid.c b/pjsip/src/pjsua-lib/pjsua_vid.c index eb74ee1..c92ec56 100644 --- a/pjsip/src/pjsua-lib/pjsua_vid.c +++ b/pjsip/src/pjsua-lib/pjsua_vid.c @@ -1,4 +1,4 @@ -/* $Id: pjsua_vid.c 4071 2012-04-24 05:40:32Z nanang $ */ +/* $Id: pjsua_vid.c 4341 2013-02-05 12:21:30Z nanang $ */ /* * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com) * @@ -705,7 +705,12 @@ pj_status_t pjsua_vid_channel_init(pjsua_call_media *call_med) return PJ_SUCCESS; } -/* Internal function: update video channel after SDP negotiation */ +/* Internal function: update video channel after SDP negotiation. + * Warning: do not use temporary/flip-flop pool, e.g: inv->pool_prov, + * for creating stream, etc, as after SDP negotiation and when + * the SDP media is not changed, the stream should remain running + * while the temporary/flip-flop pool may be released. + */ pj_status_t pjsua_vid_channel_update(pjsua_call_media *call_med, pj_pool_t *tmp_pool, pjmedia_vid_stream_info *si, @@ -1743,11 +1748,13 @@ static pj_status_t call_modify_video(pjsua_call *call, sdp->media[med_idx] = sdp_m; - /* Update SDP media line by media transport */ - status = pjmedia_transport_encode_sdp(call_med->tp, pool, - sdp, NULL, call_med->idx); - if (status != PJ_SUCCESS) - goto on_error; + if (call_med->dir == PJMEDIA_DIR_NONE) { + /* Update SDP media line by media transport */ + status = pjmedia_transport_encode_sdp(call_med->tp, pool, + sdp, NULL, call_med->idx); + if (status != PJ_SUCCESS) + goto on_error; + } on_error: if (status != PJ_SUCCESS) { @@ -2029,6 +2036,7 @@ PJ_DEF(pj_status_t) pjsua_call_set_vid_strm ( const pjsua_call_vid_strm_op_param *param) { pjsua_call *call; + pjsip_dialog *dlg; pjsua_call_vid_strm_op_param param_; pj_status_t status; @@ -2040,9 +2048,9 @@ PJ_DEF(pj_status_t) pjsua_call_set_vid_strm ( call_id, op)); pj_log_push_indent(); - PJSUA_LOCK(); - - call = &pjsua_var.calls[call_id]; + status = acquire_call("pjsua_call_set_vid_strm()", call_id, &call, &dlg); + if (status != PJ_SUCCESS) + goto on_return; if (param) { param_ = *param; @@ -2097,9 +2105,9 @@ PJ_DEF(pj_status_t) pjsua_call_set_vid_strm ( break; } - PJSUA_UNLOCK(); +on_return: + if (dlg) pjsip_dlg_dec_lock(dlg); pj_log_pop_indent(); - return status; } diff --git a/pjsip/src/test/tsx_uac_test.c b/pjsip/src/test/tsx_uac_test.c index c8f3074..eec92fc 100644 --- a/pjsip/src/test/tsx_uac_test.c +++ b/pjsip/src/test/tsx_uac_test.c @@ -1,4 +1,4 @@ -/* $Id: tsx_uac_test.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* $Id: tsx_uac_test.c 4208 2012-07-18 07:52:33Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -159,7 +159,7 @@ static struct my_timer */ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { - if (pj_strcmp2(&tsx->branch, TEST1_BRANCH_ID)==0) { + if (pj_stricmp2(&tsx->branch, TEST1_BRANCH_ID)==0) { /* * Transaction with TEST1_BRANCH_ID should terminate with transaction * timeout status. @@ -213,7 +213,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } } - } else if (pj_strcmp2(&tsx->branch, TEST2_BRANCH_ID)==0) { + } else if (pj_stricmp2(&tsx->branch, TEST2_BRANCH_ID)==0) { /* * Transaction with TEST2_BRANCH_ID should terminate with transport error. */ @@ -231,7 +231,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) test_complete = 1; } - } else if (pj_strcmp2(&tsx->branch, TEST3_BRANCH_ID)==0) { + } else if (pj_stricmp2(&tsx->branch, TEST3_BRANCH_ID)==0) { /* * This test terminates the transaction while resolver is still * running. @@ -256,7 +256,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } - } else if (pj_strcmp2(&tsx->branch, TEST4_BRANCH_ID)==0) { + } else if (pj_stricmp2(&tsx->branch, TEST4_BRANCH_ID)==0) { /* * This test simulates transport failure after several * retransmissions. @@ -284,7 +284,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } - } else if (pj_strcmp2(&tsx->branch, TEST5_BRANCH_ID)==0) { + } else if (pj_stricmp2(&tsx->branch, TEST5_BRANCH_ID)==0) { /* * This test simulates transport failure after several * retransmissions. @@ -312,7 +312,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } - } else if (pj_strcmp2(&tsx->branch, TEST6_BRANCH_ID)==0) { + } else if (pj_stricmp2(&tsx->branch, TEST6_BRANCH_ID)==0) { /* * Successfull non-INVITE transaction. */ @@ -355,7 +355,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } - } else if (pj_strcmp2(&tsx->branch, TEST7_BRANCH_ID)==0) { + } else if (pj_stricmp2(&tsx->branch, TEST7_BRANCH_ID)==0) { /* * Successfull non-INVITE transaction. */ @@ -408,7 +408,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } - } else if (pj_strcmp2(&tsx->branch, TEST8_BRANCH_ID)==0) { + } else if (pj_stricmp2(&tsx->branch, TEST8_BRANCH_ID)==0) { /* * Failed INVITE transaction. */ @@ -468,7 +468,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } - } else if (pj_strcmp2(&tsx->branch, TEST9_BRANCH_ID)==0) { + } else if (pj_stricmp2(&tsx->branch, TEST9_BRANCH_ID)==0) { /* * Failed INVITE transaction with provisional response. */ @@ -583,7 +583,7 @@ static void terminate_tsx_callback( pj_timer_heap_t *timer_heap, */ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) { - if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST1_BRANCH_ID) == 0) { + if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST1_BRANCH_ID) == 0) { /* * The TEST1_BRANCH_ID test performs the verifications for transaction * retransmission mechanism. It will not answer the incoming request @@ -651,7 +651,7 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) return PJ_TRUE; } else - if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST4_BRANCH_ID) == 0) { + if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST4_BRANCH_ID) == 0) { /* * The TEST4_BRANCH_ID test simulates transport failure after several * retransmissions. @@ -672,7 +672,7 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) } else - if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST5_BRANCH_ID) == 0) { + if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST5_BRANCH_ID) == 0) { /* * The TEST5_BRANCH_ID test simulates user terminating the transaction * after several retransmissions. @@ -703,7 +703,7 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) return PJ_TRUE; } else - if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST6_BRANCH_ID) == 0) { + if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST6_BRANCH_ID) == 0) { /* * The TEST6_BRANCH_ID test successfull non-INVITE transaction. */ @@ -728,7 +728,7 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) } else - if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST7_BRANCH_ID) == 0) { + if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST7_BRANCH_ID) == 0) { /* * The TEST7_BRANCH_ID test successfull non-INVITE transaction * with provisional response. @@ -778,7 +778,7 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) } else - if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST8_BRANCH_ID) == 0) { + if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST8_BRANCH_ID) == 0) { /* * The TEST8_BRANCH_ID test failed INVITE transaction. */ @@ -841,7 +841,7 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) } else - if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST9_BRANCH_ID) == 0) { + if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST9_BRANCH_ID) == 0) { /* * The TEST9_BRANCH_ID test failed INVITE transaction with * provisional response. diff --git a/pjsip/src/test/tsx_uas_test.c b/pjsip/src/test/tsx_uas_test.c index 045e958..6da1c4c 100644 --- a/pjsip/src/test/tsx_uas_test.c +++ b/pjsip/src/test/tsx_uas_test.c @@ -1,4 +1,4 @@ -/* $Id: tsx_uas_test.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* $Id: tsx_uas_test.c 4208 2012-07-18 07:52:33Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -352,8 +352,8 @@ static void schedule_terminate_tsx( pjsip_transaction *tsx, */ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { - if (pj_strcmp2(&tsx->branch, TEST1_BRANCH_ID)==0 || - pj_strcmp2(&tsx->branch, TEST2_BRANCH_ID)==0) + if (pj_stricmp2(&tsx->branch, TEST1_BRANCH_ID)==0 || + pj_stricmp2(&tsx->branch, TEST2_BRANCH_ID)==0) { /* * TEST1_BRANCH_ID tests that non-INVITE transaction transmits final @@ -362,7 +362,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) * * TEST2_BRANCH_ID does similar test for non-2xx final response. */ - int status_code = (pj_strcmp2(&tsx->branch, TEST1_BRANCH_ID)==0) ? + int status_code = (pj_stricmp2(&tsx->branch, TEST1_BRANCH_ID)==0) ? TEST1_STATUS_CODE : TEST2_STATUS_CODE; if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { @@ -392,7 +392,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } else - if (pj_strcmp2(&tsx->branch, TEST3_BRANCH_ID)==0) { + if (pj_stricmp2(&tsx->branch, TEST3_BRANCH_ID)==0) { /* * TEST3_BRANCH_ID tests sending provisional response. */ @@ -455,7 +455,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } } else - if (pj_strcmp2(&tsx->branch, TEST4_BRANCH_ID)==0) { + if (pj_stricmp2(&tsx->branch, TEST4_BRANCH_ID)==0) { /* * TEST4_BRANCH_ID tests receiving retransmissions in TRYING state. */ @@ -488,7 +488,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } else - if (pj_strcmp2(&tsx->branch, TEST5_BRANCH_ID)==0) { + if (pj_stricmp2(&tsx->branch, TEST5_BRANCH_ID)==0) { /* * TEST5_BRANCH_ID tests receiving retransmissions in PROCEEDING state */ @@ -525,7 +525,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } } else - if (pj_strcmp2(&tsx->branch, TEST6_BRANCH_ID)==0) { + if (pj_stricmp2(&tsx->branch, TEST6_BRANCH_ID)==0) { /* * TEST6_BRANCH_ID tests receiving retransmissions in COMPLETED state */ @@ -560,8 +560,8 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } else - if (pj_strcmp2(&tsx->branch, TEST7_BRANCH_ID)==0 || - pj_strcmp2(&tsx->branch, TEST8_BRANCH_ID)==0) + if (pj_stricmp2(&tsx->branch, TEST7_BRANCH_ID)==0 || + pj_stricmp2(&tsx->branch, TEST8_BRANCH_ID)==0) { /* * TEST7_BRANCH_ID and TEST8_BRANCH_ID test retransmission of @@ -569,7 +569,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) */ int code; - if (pj_strcmp2(&tsx->branch, TEST7_BRANCH_ID) == 0) + if (pj_stricmp2(&tsx->branch, TEST7_BRANCH_ID) == 0) code = TEST7_STATUS_CODE; else code = TEST8_STATUS_CODE; @@ -637,7 +637,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } else - if (pj_strcmp2(&tsx->branch, TEST9_BRANCH_ID)==0) { + if (pj_stricmp2(&tsx->branch, TEST9_BRANCH_ID)==0) { /* * TEST9_BRANCH_ID tests that retransmission of INVITE final response * must cease when ACK is received. @@ -701,9 +701,9 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } else - if (pj_strcmp2(&tsx->branch, TEST10_BRANCH_ID)==0 || - pj_strcmp2(&tsx->branch, TEST11_BRANCH_ID)==0 || - pj_strcmp2(&tsx->branch, TEST12_BRANCH_ID)==0) + if (pj_stricmp2(&tsx->branch, TEST10_BRANCH_ID)==0 || + pj_stricmp2(&tsx->branch, TEST11_BRANCH_ID)==0 || + pj_stricmp2(&tsx->branch, TEST12_BRANCH_ID)==0) { if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { @@ -739,8 +739,8 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) pj_str_t branch_param = rdata->msg_info.via->branch_param; pj_status_t status; - if (pj_strcmp2(&branch_param, TEST1_BRANCH_ID) == 0 || - pj_strcmp2(&branch_param, TEST2_BRANCH_ID) == 0) + if (pj_stricmp2(&branch_param, TEST1_BRANCH_ID) == 0 || + pj_stricmp2(&branch_param, TEST2_BRANCH_ID) == 0) { /* * TEST1_BRANCH_ID tests that non-INVITE transaction transmits 2xx @@ -749,7 +749,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) * * TEST2_BRANCH_ID performs similar test for non-2xx final response. */ - int status_code = (pj_strcmp2(&branch_param, TEST1_BRANCH_ID) == 0) ? + int status_code = (pj_stricmp2(&branch_param, TEST1_BRANCH_ID) == 0) ? TEST1_STATUS_CODE : TEST2_STATUS_CODE; if (msg->type == PJSIP_REQUEST_MSG) { @@ -789,7 +789,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } return PJ_TRUE; - } else if (pj_strcmp2(&branch_param, TEST3_BRANCH_ID) == 0) { + } else if (pj_stricmp2(&branch_param, TEST3_BRANCH_ID) == 0) { /* TEST3_BRANCH_ID tests provisional response. */ @@ -838,9 +838,9 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } return PJ_TRUE; - } else if (pj_strcmp2(&branch_param, TEST4_BRANCH_ID) == 0 || - pj_strcmp2(&branch_param, TEST5_BRANCH_ID) == 0 || - pj_strcmp2(&branch_param, TEST6_BRANCH_ID) == 0) + } else if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0 || + pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0 || + pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0) { /* TEST4_BRANCH_ID: absorbs retransmissions in TRYING state. */ @@ -863,12 +863,12 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); - if (pj_strcmp2(&branch_param, TEST4_BRANCH_ID) == 0) { + if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0) { - } else if (pj_strcmp2(&branch_param, TEST5_BRANCH_ID) == 0) { + } else if (pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0) { send_response(rdata, tsx, TEST5_PROVISIONAL_CODE); - } else if (pj_strcmp2(&branch_param, TEST6_BRANCH_ID) == 0) { + } else if (pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0) { PJ_LOG(4,(THIS_FILE, " sending provisional response")); send_response(rdata, tsx, TEST6_PROVISIONAL_CODE); PJ_LOG(4,(THIS_FILE, " sending final response")); @@ -882,11 +882,11 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) ++recv_count; - if (pj_strcmp2(&branch_param, TEST4_BRANCH_ID) == 0) { + if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0) { PJ_LOG(3,(THIS_FILE, " error: not expecting response!")); test_complete = -132; - } else if (pj_strcmp2(&branch_param, TEST5_BRANCH_ID) == 0) { + } else if (pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0) { if (rdata->msg_info.msg->line.status.code!=TEST5_PROVISIONAL_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code!")); @@ -898,7 +898,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) test_complete = -134; } - } else if (pj_strcmp2(&branch_param, TEST6_BRANCH_ID) == 0) { + } else if (pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0) { int code = rdata->msg_info.msg->line.status.code; @@ -927,8 +927,8 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) return PJ_TRUE; - } else if (pj_strcmp2(&branch_param, TEST7_BRANCH_ID) == 0 || - pj_strcmp2(&branch_param, TEST8_BRANCH_ID) == 0) + } else if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0 || + pj_stricmp2(&branch_param, TEST8_BRANCH_ID) == 0) { /* @@ -950,7 +950,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); - if (pj_strcmp2(&branch_param, TEST7_BRANCH_ID) == 0) { + if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0) { send_response(rdata, tsx, TEST7_STATUS_CODE); @@ -965,7 +965,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) ++recv_count; - if (pj_strcmp2(&branch_param, TEST7_BRANCH_ID) == 0) + if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0) code = TEST7_STATUS_CODE; else code = TEST8_STATUS_CODE; @@ -1013,7 +1013,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } return PJ_TRUE; - } else if (pj_strcmp2(&branch_param, TEST9_BRANCH_ID) == 0) { + } else if (pj_stricmp2(&branch_param, TEST9_BRANCH_ID) == 0) { /* * TEST9_BRANCH_ID tests that the retransmission of INVITE final @@ -1118,15 +1118,15 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } return PJ_TRUE; - } else if (pj_strcmp2(&branch_param, TEST10_BRANCH_ID) == 0 || - pj_strcmp2(&branch_param, TEST11_BRANCH_ID) == 0 || - pj_strcmp2(&branch_param, TEST12_BRANCH_ID) == 0) + } else if (pj_stricmp2(&branch_param, TEST10_BRANCH_ID) == 0 || + pj_stricmp2(&branch_param, TEST11_BRANCH_ID) == 0 || + pj_stricmp2(&branch_param, TEST12_BRANCH_ID) == 0) { int test_num, code1, code2; - if (pj_strcmp2(&branch_param, TEST10_BRANCH_ID) == 0) + if (pj_stricmp2(&branch_param, TEST10_BRANCH_ID) == 0) test_num=10, code1 = 100, code2 = 0; - else if (pj_strcmp2(&branch_param, TEST11_BRANCH_ID) == 0) + else if (pj_stricmp2(&branch_param, TEST11_BRANCH_ID) == 0) test_num=11, code1 = 100, code2 = 200; else test_num=12, code1 = 200, code2 = 0; diff --git a/pjsip/src/test/uri_test.c b/pjsip/src/test/uri_test.c index 8527faa..c8b0b3e 100644 --- a/pjsip/src/test/uri_test.c +++ b/pjsip/src/test/uri_test.c @@ -1,4 +1,4 @@ -/* $Id: uri_test.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* $Id: uri_test.c 4210 2012-07-19 01:00:07Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -67,6 +67,7 @@ static pjsip_uri *create_uri14( pj_pool_t *pool ); static pjsip_uri *create_uri15( pj_pool_t *pool ); static pjsip_uri *create_uri16( pj_pool_t *pool ); static pjsip_uri *create_uri17( pj_pool_t *pool ); +static pjsip_uri *create_uri18( pj_pool_t *pool ); static pjsip_uri *create_uri25( pj_pool_t *pool ); static pjsip_uri *create_uri26( pj_pool_t *pool ); static pjsip_uri *create_uri27( pj_pool_t *pool ); @@ -81,6 +82,7 @@ static pjsip_uri *create_uri35( pj_pool_t *pool ); static pjsip_uri *create_uri36( pj_pool_t *pool ); static pjsip_uri *create_uri37( pj_pool_t *pool ); static pjsip_uri *create_uri38( pj_pool_t *pool ); +static pjsip_uri *create_uri39( pj_pool_t *pool ); static pjsip_uri *create_dummy( pj_pool_t *pool ); #define ERR_NOT_EQUAL -1001 @@ -349,6 +351,12 @@ struct uri_test "\xC0\x81 ", &create_uri38, "\"\xC0\x81\" " + }, + { + /* Even number of backslash before end quote in display name. */ + PJ_SUCCESS, + "\"User\\\\\" ", + &create_uri39, } }; @@ -759,6 +767,20 @@ static pjsip_uri *create_uri38( pj_pool_t *pool ) } +/* "\"User\\\\\" " */ +static pjsip_uri *create_uri39(pj_pool_t *pool) +{ + pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); + pjsip_sip_uri *url; + + url = pjsip_sip_uri_create(pool, 0); + name_addr->uri = (pjsip_uri*) url; + + pj_strdup2(pool, &name_addr->display, "User\\\\"); + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)name_addr; +} + static pjsip_uri *create_dummy(pj_pool_t *pool) { PJ_UNUSED_ARG(pool); -- cgit v1.2.3