/* $Id$ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PJSIP_EX_NO_MEMORY pj_NO_MEMORY_EXCEPTION() #define THIS_FILE "sip_endpoint.c" #define MAX_METHODS 32 /* List of SIP endpoint exit callback. */ typedef struct exit_cb { PJ_DECL_LIST_MEMBER (struct exit_cb); pjsip_endpt_exit_callback func; } exit_cb; /** * The SIP endpoint. */ struct pjsip_endpoint { /** Pool to allocate memory for the endpoint. */ pj_pool_t *pool; /** Mutex for the pool, hash table, and event list/queue. */ pj_mutex_t *mutex; /** Pool factory. */ pj_pool_factory *pf; /** Name. */ pj_str_t name; /** Timer heap. */ pj_timer_heap_t *timer_heap; /** Transport manager. */ pjsip_tpmgr *transport_mgr; /** Ioqueue. */ pj_ioqueue_t *ioqueue; /** Last ioqueue err */ pj_status_t ioq_last_err; /** DNS Resolver. */ pjsip_resolver_t *resolver; /** Modules lock. */ pj_rwmutex_t *mod_mutex; /** Modules. */ pjsip_module *modules[PJSIP_MAX_MODULE]; /** Module list, sorted by priority. */ pjsip_module module_list; /** Capability header list. */ pjsip_hdr cap_hdr; /** Additional request headers. */ pjsip_hdr req_hdr; /** List of exit callback. */ exit_cb exit_cb_list; }; #if defined(PJSIP_SAFE_MODULE) && PJSIP_SAFE_MODULE!=0 # define LOCK_MODULE_ACCESS(ept) pj_rwmutex_lock_read(ept->mod_mutex) # define UNLOCK_MODULE_ACCESS(ept) pj_rwmutex_unlock_read(ept->mod_mutex) #else # define LOCK_MODULE_ACCESS(endpt) # define UNLOCK_MODULE_ACCESS(endpt) #endif /* * Prototypes. */ static void endpt_on_rx_msg( pjsip_endpoint*, pj_status_t, pjsip_rx_data*); static pj_status_t endpt_on_tx_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata ); static pj_status_t unload_module(pjsip_endpoint *endpt, pjsip_module *mod); /* Defined in sip_parser.c */ void init_sip_parser(void); void deinit_sip_parser(void); /* Defined in sip_tel_uri.c */ pj_status_t pjsip_tel_uri_subsys_init(void); /* * This is the global handler for memory allocation failure, for pools that * are created by the endpoint (by default, all pools ARE allocated by * endpoint). The error is handled by throwing exception, and hopefully, * the exception will be handled by the application (or this library). */ static void pool_callback( pj_pool_t *pool, pj_size_t size ) { PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(size); PJ_THROW(PJSIP_EX_NO_MEMORY); } /* Compare module name, used for searching module based on name. */ static int cmp_mod_name(void *name, const void *mod) { return pj_stricmp((const pj_str_t*)name, &((pjsip_module*)mod)->name); } /* * Register new module to the endpoint. * The endpoint will then call the load and start function in the module to * properly initialize the module, and assign a unique module ID for the * module. */ PJ_DEF(pj_status_t) pjsip_endpt_register_module( pjsip_endpoint *endpt, pjsip_module *mod ) { pj_status_t status = PJ_SUCCESS; pjsip_module *m; unsigned i; pj_rwmutex_lock_write(endpt->mod_mutex); /* Make sure that this module has not been registered. */ PJ_ASSERT_ON_FAIL( pj_list_find_node(&endpt->module_list, mod) == NULL, {status = PJ_EEXISTS; goto on_return;}); /* Make sure that no module with the same name has been registered. */ PJ_ASSERT_ON_FAIL( pj_list_search(&endpt->module_list, &mod->name, &cmp_mod_name)==NULL, {status = PJ_EEXISTS; goto on_return; }); /* Find unused ID for this module. */ for (i=0; imodules); ++i) { if (endpt->modules[i] == NULL) break; } if (i == PJ_ARRAY_SIZE(endpt->modules)) { pj_assert(!"Too many modules registered!"); status = PJ_ETOOMANY; goto on_return; } /* Assign the ID. */ mod->id = i; /* Try to load the module. */ if (mod->load) { status = (*mod->load)(endpt); if (status != PJ_SUCCESS) goto on_return; } /* Try to start the module. */ if (mod->start) { status = (*mod->start)(); if (status != PJ_SUCCESS) goto on_return; } /* Save the module. */ endpt->modules[i] = mod; /* Put in the module list, sorted by priority. */ m = endpt->module_list.next; while (m != &endpt->module_list) { if (m->priority > mod->priority) break; m = m->next; } pj_list_insert_before(m, mod); /* Done. */ PJ_LOG(4,(THIS_FILE, "Module \"%.*s\" registered", (int)mod->name.slen, mod->name.ptr)); on_return: pj_rwmutex_unlock_write(endpt->mod_mutex); return status; } /* * Unregister a module from the endpoint. * The endpoint will then call the stop and unload function in the module to * properly shutdown the module. */ PJ_DEF(pj_status_t) pjsip_endpt_unregister_module( pjsip_endpoint *endpt, pjsip_module *mod ) { pj_status_t status; pj_rwmutex_lock_write(endpt->mod_mutex); /* Make sure the module exists in the list. */ PJ_ASSERT_ON_FAIL( pj_list_find_node(&endpt->module_list, mod) == mod, {status = PJ_ENOTFOUND;goto on_return;} ); /* Make sure the module exists in the array. */ PJ_ASSERT_ON_FAIL( mod->id>=0 && mod->id<(int)PJ_ARRAY_SIZE(endpt->modules) && endpt->modules[mod->id] == mod, {status = PJ_ENOTFOUND; goto on_return;}); /* Try to stop the module. */ if (mod->stop) { status = (*mod->stop)(); if (status != PJ_SUCCESS) goto on_return; } /* Unload module */ status = unload_module(endpt, mod); on_return: pj_rwmutex_unlock_write(endpt->mod_mutex); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(3,(THIS_FILE, "Module \"%.*s\" can not be unregistered: %s", (int)mod->name.slen, mod->name.ptr, errmsg)); } return status; } static pj_status_t unload_module(pjsip_endpoint *endpt, pjsip_module *mod) { pj_status_t status; /* Try to unload the module. */ if (mod->unload) { status = (*mod->unload)(); if (status != PJ_SUCCESS) return status; } /* Module MUST NOT set module ID to -1. */ pj_assert(mod->id >= 0); /* Remove module from array. */ endpt->modules[mod->id] = NULL; /* Remove module from list. */ pj_list_erase(mod); /* Set module Id to -1. */ mod->id = -1; /* Done. */ status = PJ_SUCCESS; PJ_LOG(4,(THIS_FILE, "Module \"%.*s\" unregistered", (int)mod->name.slen, mod->name.ptr)); return status; } /* * Get the value of the specified capability header field. */ PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_capability( pjsip_endpoint *endpt, int htype, const pj_str_t *hname) { pjsip_hdr *hdr = endpt->cap_hdr.next; /* Check arguments. */ PJ_ASSERT_RETURN(endpt != NULL, NULL); PJ_ASSERT_RETURN(htype != PJSIP_H_OTHER || hname, NULL); if (htype != PJSIP_H_OTHER) { while (hdr != &endpt->cap_hdr) { if (hdr->type == htype) return hdr; hdr = hdr->next; } } return NULL; } /* * Check if the specified capability is supported. */ PJ_DEF(pj_bool_t) pjsip_endpt_has_capability( pjsip_endpoint *endpt, int htype, const pj_str_t *hname, const pj_str_t *token) { const pjsip_generic_array_hdr *hdr; unsigned i; hdr = (const pjsip_generic_array_hdr*) pjsip_endpt_get_capability(endpt, htype, hname); if (!hdr) return PJ_FALSE; PJ_ASSERT_RETURN(token != NULL, PJ_FALSE); for (i=0; icount; ++i) { if (!pj_stricmp(&hdr->values[i], token)) return PJ_TRUE; } return PJ_FALSE; } /* * Add or register new capabilities as indicated by the tags to the * appropriate header fields in the endpoint. */ PJ_DEF(pj_status_t) pjsip_endpt_add_capability( pjsip_endpoint *endpt, pjsip_module *mod, int htype, const pj_str_t *hname, unsigned count, const pj_str_t tags[]) { pjsip_generic_array_hdr *hdr; unsigned i; PJ_UNUSED_ARG(mod); /* Check arguments. */ PJ_ASSERT_RETURN(endpt!=NULL && count>0 && tags, PJ_EINVAL); PJ_ASSERT_RETURN(count <= PJSIP_GENERIC_ARRAY_MAX_COUNT, PJ_ETOOMANY); PJ_ASSERT_RETURN(htype==PJSIP_H_ACCEPT || htype==PJSIP_H_ALLOW || htype==PJSIP_H_SUPPORTED, PJ_EINVAL); /* Find the header. */ hdr = (pjsip_generic_array_hdr*) pjsip_endpt_get_capability(endpt, htype, hname); /* Create the header when it's not present */ if (hdr == NULL) { switch (htype) { case PJSIP_H_ACCEPT: hdr = pjsip_accept_hdr_create(endpt->pool); break; case PJSIP_H_ALLOW: hdr = pjsip_allow_hdr_create(endpt->pool); break; case PJSIP_H_SUPPORTED: hdr = pjsip_supported_hdr_create(endpt->pool); break; default: return PJ_EINVAL; } if (hdr) { pj_list_push_back(&endpt->cap_hdr, hdr); } } /* Add the tags to the header. */ for (i=0; ipool, &hdr->values[hdr->count], &tags[i]); ++hdr->count; } /* Done. */ return PJ_SUCCESS; } /* * Get additional headers to be put in outgoing request message. */ PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt) { return &endpt->req_hdr; } /* * Initialize endpoint. */ PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf, const char *name, pjsip_endpoint **p_endpt) { pj_status_t status; pj_pool_t *pool; pjsip_endpoint *endpt; pjsip_max_fwd_hdr *mf_hdr; pj_lock_t *lock = NULL; status = pj_register_strerror(PJSIP_ERRNO_START, PJ_ERRNO_SPACE_SIZE, &pjsip_strerror); pj_assert(status == PJ_SUCCESS); PJ_LOG(5, (THIS_FILE, "Creating endpoint instance...")); *p_endpt = NULL; /* Create pool */ pool = pj_pool_create(pf, "pept%p", PJSIP_POOL_LEN_ENDPT, PJSIP_POOL_INC_ENDPT, &pool_callback); if (!pool) return PJ_ENOMEM; /* Create endpoint. */ endpt = PJ_POOL_ZALLOC_T(pool, pjsip_endpoint); endpt->pool = pool; endpt->pf = pf; /* Init modules list. */ pj_list_init(&endpt->module_list); /* Initialize exit callback list. */ pj_list_init(&endpt->exit_cb_list); /* Create R/W mutex for module manipulation. */ status = pj_rwmutex_create(endpt->pool, "ept%p", &endpt->mod_mutex); if (status != PJ_SUCCESS) goto on_error; /* Init parser. */ init_sip_parser(); /* Init tel: uri */ pjsip_tel_uri_subsys_init(); /* Get name. */ if (name != NULL) { pj_str_t temp; pj_strdup_with_null(endpt->pool, &endpt->name, pj_cstr(&temp, name)); } else { pj_strdup_with_null(endpt->pool, &endpt->name, pj_gethostname()); } /* Create mutex for the events, etc. */ status = pj_mutex_create_recursive( endpt->pool, "ept%p", &endpt->mutex ); if (status != PJ_SUCCESS) { goto on_error; } /* Create timer heap to manage all timers within this endpoint. */ status = pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT, &endpt->timer_heap); if (status != PJ_SUCCESS) { goto on_error; } /* Set recursive lock for the timer heap. */ status = pj_lock_create_recursive_mutex( endpt->pool, "edpt%p", &lock); if (status != PJ_SUCCESS) { goto on_error; } pj_timer_heap_set_lock(endpt->timer_heap, lock, PJ_TRUE); /* Set maximum timed out entries to process in a single poll. */ pj_timer_heap_set_max_timed_out_per_poll(endpt->timer_heap, PJSIP_MAX_TIMED_OUT_ENTRIES); /* Create ioqueue. */ status = pj_ioqueue_create( endpt->pool, PJSIP_MAX_TRANSPORTS, &endpt->ioqueue); if (status != PJ_SUCCESS) { goto on_error; } /* Create transport manager. */ status = pjsip_tpmgr_create( endpt->pool, endpt, &endpt_on_rx_msg, &endpt_on_tx_msg, &endpt->transport_mgr); if (status != PJ_SUCCESS) { goto on_error; } /* Create asynchronous DNS resolver. */ status = pjsip_resolver_create(endpt->pool, &endpt->resolver); if (status != PJ_SUCCESS) { PJ_PERROR(4, (THIS_FILE, status, "Error creating resolver instance")); goto on_error; } /* Initialize request headers. */ pj_list_init(&endpt->req_hdr); /* Add "Max-Forwards" for request header. */ mf_hdr = pjsip_max_fwd_hdr_create(endpt->pool, PJSIP_MAX_FORWARDS_VALUE); pj_list_insert_before( &endpt->req_hdr, mf_hdr); /* Initialize capability header list. */ pj_list_init(&endpt->cap_hdr); /* Done. */ *p_endpt = endpt; return status; on_error: if (endpt->transport_mgr) { pjsip_tpmgr_destroy(endpt->transport_mgr); endpt->transport_mgr = NULL; } if (endpt->ioqueue) { pj_ioqueue_destroy(endpt->ioqueue); endpt->ioqueue = NULL; } if (endpt->timer_heap) { pj_timer_heap_destroy(endpt->timer_heap); endpt->timer_heap = NULL; } if (endpt->mutex) { pj_mutex_destroy(endpt->mutex); endpt->mutex = NULL; } deinit_sip_parser(); if (endpt->mod_mutex) { pj_rwmutex_destroy(endpt->mod_mutex); endpt->mod_mutex = NULL; } pj_pool_release( endpt->pool ); PJ_PERROR(4, (THIS_FILE, status, "Error creating endpoint")); return status; } /* * Destroy endpoint. */ PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt) { pjsip_module *mod; exit_cb *ecb; PJ_LOG(5, (THIS_FILE, "Destroying endpoint instance..")); /* Phase 1: stop all modules */ mod = endpt->module_list.prev; while (mod != &endpt->module_list) { pjsip_module *prev = mod->prev; if (mod->stop) { (*mod->stop)(); } mod = prev; } /* Phase 2: unload modules. */ mod = endpt->module_list.prev; while (mod != &endpt->module_list) { pjsip_module *prev = mod->prev; unload_module(endpt, mod); mod = prev; } /* Destroy resolver */ pjsip_resolver_destroy(endpt->resolver); /* Shutdown and destroy all transports. */ pjsip_tpmgr_destroy(endpt->transport_mgr); /* Destroy ioqueue */ pj_ioqueue_destroy(endpt->ioqueue); /* Destroy timer heap */ #if PJ_TIMER_DEBUG pj_timer_heap_dump(endpt->timer_heap); #endif pj_timer_heap_destroy(endpt->timer_heap); /* Call all registered exit callbacks */ ecb = endpt->exit_cb_list.next; while (ecb != &endpt->exit_cb_list) { (*ecb->func)(endpt); ecb = ecb->next; } /* Delete endpoint mutex. */ pj_mutex_destroy(endpt->mutex); /* Deinit parser */ deinit_sip_parser(); /* Delete module's mutex */ pj_rwmutex_destroy(endpt->mod_mutex); /* Finally destroy pool. */ pj_pool_release(endpt->pool); PJ_LOG(4, (THIS_FILE, "Endpoint %p destroyed", endpt)); } /* * Get endpoint name. */ PJ_DEF(const pj_str_t*) pjsip_endpt_name(const pjsip_endpoint *endpt) { return &endpt->name; } /* * Create new pool. */ PJ_DEF(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt, const char *pool_name, pj_size_t initial, pj_size_t increment ) { pj_pool_t *pool; /* Lock endpoint mutex. */ /* No need to lock mutex. Factory is thread safe. pj_mutex_lock(endpt->mutex); */ /* Create pool */ pool = pj_pool_create( endpt->pf, pool_name, initial, increment, &pool_callback); /* Unlock mutex. */ /* No need to lock mutex. Factory is thread safe. pj_mutex_unlock(endpt->mutex); */ if (!pool) { PJ_LOG(4, (THIS_FILE, "Unable to create pool %s!", pool_name)); } return pool; } /* * Return back pool to endpoint's pool manager to be either destroyed or * recycled. */ PJ_DEF(void) pjsip_endpt_release_pool( pjsip_endpoint *endpt, pj_pool_t *pool ) { PJ_LOG(6, (THIS_FILE, "Releasing pool %s", pj_pool_getobjname(pool))); /* Don't need to acquire mutex since pool factory is thread safe pj_mutex_lock(endpt->mutex); */ pj_pool_release( pool ); PJ_UNUSED_ARG(endpt); /* pj_mutex_unlock(endpt->mutex); */ } PJ_DEF(pj_status_t) pjsip_endpt_handle_events2(pjsip_endpoint *endpt, const pj_time_val *max_timeout, unsigned *p_count) { enum { MAX_TIMEOUT_ON_ERR = 10 }; /* timeout is 'out' var. This just to make compiler happy. */ pj_time_val timeout = { 0, 0}; unsigned count = 0, net_event_count = 0; int c; PJ_LOG(6, (THIS_FILE, "pjsip_endpt_handle_events()")); /* Poll the timer. The timer heap has its own mutex for better * granularity, so we don't need to lock end endpoint. */ timeout.sec = timeout.msec = 0; c = pj_timer_heap_poll( endpt->timer_heap, &timeout ); if (c > 0) count += c; /* timer_heap_poll should never ever returns negative value, or otherwise * ioqueue_poll() will block forever! */ pj_assert(timeout.sec >= 0 && timeout.msec >= 0); if (timeout.msec >= 1000) timeout.msec = 999; /* If caller specifies maximum time to wait, then compare the value with * the timeout to wait from timer, and use the minimum value. */ if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) { timeout = *max_timeout; } /* Poll ioqueue. * Repeat polling the ioqueue while we have immediate events, because * timer heap may process more than one events, so if we only process * one network events at a time (such as when IOCP backend is used), * the ioqueue may have trouble keeping up with the request rate. * * For example, for each send() request, one network event will be * reported by ioqueue for the send() completion. If we don't poll * the ioqueue often enough, the send() completion will not be * reported in timely manner. */ do { c = pj_ioqueue_poll( endpt->ioqueue, &timeout); if (c < 0) { pj_status_t err = pj_get_netos_error(); #if PJSIP_HANDLE_EVENTS_HAS_SLEEP_ON_ERR unsigned msec = PJ_TIME_VAL_MSEC(timeout); pj_thread_sleep(PJ_MIN(msec, MAX_TIMEOUT_ON_ERR)); #endif if (p_count) *p_count = count; return err; } else if (c == 0) { break; } else { net_event_count += c; timeout.sec = timeout.msec = 0; } } while (c > 0 && net_event_count < PJSIP_MAX_NET_EVENTS); count += net_event_count; if (p_count) *p_count = count; return PJ_SUCCESS; } /* * Handle events. */ PJ_DEF(pj_status_t) pjsip_endpt_handle_events(pjsip_endpoint *endpt, const pj_time_val *max_timeout) { return pjsip_endpt_handle_events2(endpt, max_timeout, NULL); } /* * Schedule timer. */ #if PJ_TIMER_DEBUG PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer_dbg(pjsip_endpoint *endpt, pj_timer_entry *entry, const pj_time_val *delay, const char *src_file, int src_line) { PJ_LOG(6, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)", entry, delay->sec, delay->msec)); return pj_timer_heap_schedule_dbg(endpt->timer_heap, entry, delay, src_file, src_line); } #else PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt, pj_timer_entry *entry, const pj_time_val *delay ) { PJ_LOG(6, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)", entry, delay->sec, delay->msec)); return pj_timer_heap_schedule( endpt->timer_heap, entry, delay ); } #endif /* * Schedule timer with group lock. */ #if PJ_TIMER_DEBUG PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer_w_grp_lock_dbg( pjsip_endpoint *endpt, pj_timer_entry *entry, const pj_time_val *delay, int id_val, pj_grp_lock_t *grp_lock, const char *src_file, int src_line) { PJ_LOG(6, (THIS_FILE, "pjsip_endpt_schedule_timer_w_grp_lock" "(entry=%p, delay=%u.%u, grp_lock=%p)", entry, delay->sec, delay->msec, grp_lock)); return pj_timer_heap_schedule_w_grp_lock_dbg(endpt->timer_heap, entry, delay, id_val, grp_lock, src_file, src_line); } #else PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer_w_grp_lock( pjsip_endpoint *endpt, pj_timer_entry *entry, const pj_time_val *delay, int id_val, pj_grp_lock_t *grp_lock ) { PJ_LOG(6, (THIS_FILE, "pjsip_endpt_schedule_timer_w_grp_lock" "(entry=%p, delay=%u.%u, grp_lock=%p)", entry, delay->sec, delay->msec, grp_lock)); return pj_timer_heap_schedule_w_grp_lock( endpt->timer_heap, entry, delay, id_val, grp_lock ); } #endif /* * Cancel the previously registered timer. */ PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, pj_timer_entry *entry ) { PJ_LOG(6, (THIS_FILE, "pjsip_endpt_cancel_timer(entry=%p)", entry)); pj_timer_heap_cancel( endpt->timer_heap, entry ); } /* * Get the timer heap instance of the SIP endpoint. */ 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. */ 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; PJ_UNUSED_ARG(msg); if (status != PJ_SUCCESS) { char info[30]; char errmsg[PJ_ERR_MSG_SIZE]; info[0] = '\0'; if (status == PJSIP_EMISSINGHDR) { pj_str_t p; p.ptr = info; p.slen = 0; if (rdata->msg_info.cid == NULL || rdata->msg_info.cid->id.slen) pj_strcpy2(&p, "Call-ID"); if (rdata->msg_info.from == NULL) pj_strcpy2(&p, " From"); if (rdata->msg_info.to == NULL) pj_strcpy2(&p, " To"); if (rdata->msg_info.via == NULL) pj_strcpy2(&p, " Via"); if (rdata->msg_info.cseq == NULL) pj_strcpy2(&p, " CSeq"); p.ptr[p.slen] = '\0'; } pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(1, (THIS_FILE, "Error processing packet from %s:%d: %s %s [code %d]:\n" "%.*s\n" "-- end of packet.", rdata->pkt_info.src_name, rdata->pkt_info.src_port, errmsg, info, status, (int)rdata->msg_info.len, rdata->msg_info.msg_buf)); return; } PJ_LOG(5, (THIS_FILE, "Processing incoming message: %s", pjsip_rx_data_get_info(rdata))); pj_log_push_indent(); #if defined(PJSIP_CHECK_VIA_SENT_BY) && PJSIP_CHECK_VIA_SENT_BY != 0 /* For response, check that the value in Via sent-by match the transport. * If not matched, silently drop the response. * Ref: RFC3261 Section 18.1.2 Receiving Response */ if (msg->type == PJSIP_RESPONSE_MSG) { const pj_str_t *local_addr; int port = rdata->msg_info.via->sent_by.port; pj_bool_t mismatch = PJ_FALSE; if (port == 0) { pjsip_transport_type_e type; type = (pjsip_transport_type_e)rdata->tp_info.transport->key.type; port = pjsip_transport_get_default_port_for_type(type); } local_addr = &rdata->tp_info.transport->local_name.host; if (pj_strcmp(&rdata->msg_info.via->sent_by.host, local_addr) != 0) { /* The RFC says that we should drop response when sent-by * address mismatch. But it could happen (e.g. with SER) when * endpoint with private IP is sending request to public * server. mismatch = PJ_TRUE; */ } else if (port != rdata->tp_info.transport->local_name.port) { /* Port or address mismatch, we should discard response */ /* But we saw one implementation (we don't want to name it to * protect the innocence) which put wrong sent-by port although * the "rport" parameter is correct. * So we discard the response only if the port doesn't match * both the port in sent-by and rport. We try to be lenient here! */ if (rdata->msg_info.via->rport_param != rdata->tp_info.transport->local_name.port) mismatch = PJ_TRUE; else { PJ_LOG(4,(THIS_FILE, "Message %s from %s has mismatch port in " "sent-by but the rport parameter is " "correct", pjsip_rx_data_get_info(rdata), rdata->pkt_info.src_name)); } } if (mismatch) { PJ_TODO(ENDPT_REPORT_WHEN_DROPPING_MESSAGE); PJ_LOG(4,(THIS_FILE, "Dropping response %s from %s:%d because " "sent-by is mismatch", pjsip_rx_data_get_info(rdata), rdata->pkt_info.src_name, rdata->pkt_info.src_port)); pj_log_pop_indent(); return; } } #endif pjsip_process_rdata_param_default(&proc_prm); proc_prm.silent = PJ_TRUE; pjsip_endpt_process_rx_data(endpt, rdata, &proc_prm, &handled); /* 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)); } /* Must clear mod_data before returning rdata to transport, since * rdata may be reused. */ pj_bzero(&rdata->endpt_info, sizeof(rdata->endpt_info)); pj_log_pop_indent(); } /* * This callback is called by transport manager before message is sent. * Modules may inspect the message before it's actually sent. */ static pj_status_t endpt_on_tx_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata ) { pj_status_t status = PJ_SUCCESS; pjsip_module *mod; /* Distribute to modules, starting from modules with LOWEST priority */ LOCK_MODULE_ACCESS(endpt); mod = endpt->module_list.prev; if (tdata->msg->type == PJSIP_REQUEST_MSG) { while (mod != &endpt->module_list) { if (mod->on_tx_request) status = (*mod->on_tx_request)(tdata); if (status != PJ_SUCCESS) break; mod = mod->prev; } } else { while (mod != &endpt->module_list) { if (mod->on_tx_response) status = (*mod->on_tx_response)(tdata); if (status != PJ_SUCCESS) break; mod = mod->prev; } } UNLOCK_MODULE_ACCESS(endpt); return status; } /* * Create transmit data buffer. */ PJ_DEF(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt, pjsip_tx_data **p_tdata) { return pjsip_tx_data_create(endpt->transport_mgr, p_tdata); } /* * Create the DNS resolver instance. */ PJ_DEF(pj_status_t) pjsip_endpt_create_resolver(pjsip_endpoint *endpt, pj_dns_resolver **p_resv) { #if PJSIP_HAS_RESOLVER PJ_ASSERT_RETURN(endpt && p_resv, PJ_EINVAL); return pj_dns_resolver_create( endpt->pf, NULL, 0, endpt->timer_heap, endpt->ioqueue, p_resv); #else PJ_UNUSED_ARG(endpt); PJ_UNUSED_ARG(p_resv); pj_assert(!"Resolver is disabled (PJSIP_HAS_RESOLVER==0)"); return PJ_EINVALIDOP; #endif } /* * Set DNS resolver to be used by the SIP resolver. */ PJ_DEF(pj_status_t) pjsip_endpt_set_resolver( pjsip_endpoint *endpt, pj_dns_resolver *resv) { return pjsip_resolver_set_resolver(endpt->resolver, resv); } /* * Set DNS external resolver implementation to be used by the SIP resolver. */ PJ_DEF(pj_status_t) pjsip_endpt_set_ext_resolver(pjsip_endpoint *endpt, pjsip_ext_resolver *ext_res) { return pjsip_resolver_set_ext_resolver(endpt->resolver, ext_res); } /* * Get the DNS resolver being used by the SIP resolver. */ PJ_DEF(pj_dns_resolver*) pjsip_endpt_get_resolver(pjsip_endpoint *endpt) { PJ_ASSERT_RETURN(endpt, NULL); return pjsip_resolver_get_resolver(endpt->resolver); } /* * Resolve */ PJ_DEF(void) pjsip_endpt_resolve( pjsip_endpoint *endpt, pj_pool_t *pool, pjsip_host_info *target, void *token, pjsip_resolver_callback *cb) { pjsip_resolve( endpt->resolver, pool, target, token, cb); } /* * Get transport manager. */ PJ_DEF(pjsip_tpmgr*) pjsip_endpt_get_tpmgr(pjsip_endpoint *endpt) { return endpt->transport_mgr; } /* * Get ioqueue instance. */ PJ_DEF(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt) { return endpt->ioqueue; } /* * Find/create transport. */ PJ_DEF(pj_status_t) pjsip_endpt_acquire_transport(pjsip_endpoint *endpt, pjsip_transport_type_e type, const pj_sockaddr_t *remote, int addr_len, const pjsip_tpselector *sel, pjsip_transport **transport) { return pjsip_tpmgr_acquire_transport(endpt->transport_mgr, type, remote, addr_len, sel, transport); } /* * Find/create transport. */ PJ_DEF(pj_status_t) pjsip_endpt_acquire_transport2(pjsip_endpoint *endpt, pjsip_transport_type_e type, const pj_sockaddr_t *remote, int addr_len, const pjsip_tpselector *sel, pjsip_tx_data *tdata, pjsip_transport **transport) { return pjsip_tpmgr_acquire_transport2(endpt->transport_mgr, type, remote, addr_len, sel, tdata, transport); } /* * Report error. */ PJ_DEF(void) pjsip_endpt_log_error( pjsip_endpoint *endpt, const char *sender, pj_status_t error_code, const char *format, ... ) { #if PJ_LOG_MAX_LEVEL > 0 char newformat[256]; pj_size_t len; va_list marker; va_start(marker, format); PJ_UNUSED_ARG(endpt); len = pj_ansi_strlen(format); if (len < (int)sizeof(newformat)-30) { pj_str_t errstr; pj_ansi_strcpy(newformat, format); pj_ansi_snprintf(newformat+len, sizeof(newformat)-len-1, ": [err %d] ", error_code); len += pj_ansi_strlen(newformat+len); errstr = pj_strerror( error_code, newformat+len, sizeof(newformat)-len-1); len += errstr.slen; newformat[len] = '\0'; pj_log(sender, 1, newformat, marker); } else { pj_log(sender, 1, format, marker); } va_end(marker); #else PJ_UNUSED_ARG(format); PJ_UNUSED_ARG(error_code); PJ_UNUSED_ARG(sender); PJ_UNUSED_ARG(endpt); #endif } /* * Dump endpoint. */ PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail ) { #if PJ_LOG_MAX_LEVEL >= 3 PJ_LOG(5, (THIS_FILE, "pjsip_endpt_dump()")); /* Lock mutex. */ pj_mutex_lock(endpt->mutex); PJ_LOG(3, (THIS_FILE, "Dumping endpoint %p:", endpt)); /* Dumping pool factory. */ pj_pool_factory_dump(endpt->pf, detail); /* Pool health. */ PJ_LOG(3, (THIS_FILE," Endpoint pool capacity=%u, used_size=%u", pj_pool_get_capacity(endpt->pool), pj_pool_get_used_size(endpt->pool))); /* Resolver */ #if PJSIP_HAS_RESOLVER if (pjsip_endpt_get_resolver(endpt)) { pj_dns_resolver_dump(pjsip_endpt_get_resolver(endpt), detail); } #endif /* Transports. */ pjsip_tpmgr_dump_transports( endpt->transport_mgr ); /* Timer. */ #if PJ_TIMER_DEBUG pj_timer_heap_dump(endpt->timer_heap); #else PJ_LOG(3,(THIS_FILE, " Timer heap has %u entries", pj_timer_heap_count(endpt->timer_heap))); #endif /* Unlock mutex. */ pj_mutex_unlock(endpt->mutex); #else PJ_UNUSED_ARG(endpt); PJ_UNUSED_ARG(detail); PJ_LOG(3,(THIS_FILE, "pjsip_end_dump: can't dump because it's disabled.")); #endif } PJ_DEF(pj_status_t) pjsip_endpt_atexit( pjsip_endpoint *endpt, pjsip_endpt_exit_callback func) { exit_cb *new_cb; PJ_ASSERT_RETURN(endpt && func, PJ_EINVAL); new_cb = PJ_POOL_ZALLOC_T(endpt->pool, exit_cb); new_cb->func = func; pj_mutex_lock(endpt->mutex); pj_list_push_back(&endpt->exit_cb_list, new_cb); pj_mutex_unlock(endpt->mutex); return PJ_SUCCESS; }