1 /* $Id$ */
2 /*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20 #include <pjsip/sip_endpoint.h>
21 #include <pjsip/sip_transaction.h>
22 #include <pjsip/sip_private.h>
23 #include <pjsip/sip_event.h>
24 #include <pjsip/sip_resolve.h>
25 #include <pjsip/sip_module.h>
26 #include <pjsip/sip_util.h>
27 #include <pjsip/sip_errno.h>
28 #include <pj/except.h>
29 #include <pj/log.h>
30 #include <pj/string.h>
31 #include <pj/os.h>
32 #include <pj/pool.h>
33 #include <pj/hash.h>
34 #include <pj/assert.h>
35 #include <pj/errno.h>
36 #include <pj/lock.h>
37 #include <pj/math.h>
38
39 #define PJSIP_EX_NO_MEMORY pj_NO_MEMORY_EXCEPTION()
40 #define THIS_FILE "sip_endpoint.c"
41
42 #define MAX_METHODS 32
43
44
45 /* List of SIP endpoint exit callback. */
46 typedef struct exit_cb
47 {
48 PJ_DECL_LIST_MEMBER (struct exit_cb);
49 pjsip_endpt_exit_callback func;
50 } exit_cb;
51
52
53 /**
54 * The SIP endpoint.
55 */
56 struct pjsip_endpoint
57 {
58 /** Pool to allocate memory for the endpoint. */
59 pj_pool_t *pool;
60
61 /** Mutex for the pool, hash table, and event list/queue. */
62 pj_mutex_t *mutex;
63
64 /** Pool factory. */
65 pj_pool_factory *pf;
66
67 /** Name. */
68 pj_str_t name;
69
70 /** Timer heap. */
71 pj_timer_heap_t *timer_heap;
72
73 /** Transport manager. */
74 pjsip_tpmgr *transport_mgr;
75
76 /** Ioqueue. */
77 pj_ioqueue_t *ioqueue;
78
79 /** Last ioqueue err */
80 pj_status_t ioq_last_err;
81
82 /** DNS Resolver. */
83 pjsip_resolver_t *resolver;
84
85 /** Modules lock. */
86 pj_rwmutex_t *mod_mutex;
87
88 /** Modules. */
89 pjsip_module *modules[PJSIP_MAX_MODULE];
90
91 /** Module list, sorted by priority. */
92 pjsip_module module_list;
93
94 /** Capability header list. */
95 pjsip_hdr cap_hdr;
96
97 /** Additional request headers. */
98 pjsip_hdr req_hdr;
99
100 /** List of exit callback. */
101 exit_cb exit_cb_list;
102 };
103
104
105 #if defined(PJSIP_SAFE_MODULE) && PJSIP_SAFE_MODULE!=0
106 # define LOCK_MODULE_ACCESS(ept) pj_rwmutex_lock_read(ept->mod_mutex)
107 # define UNLOCK_MODULE_ACCESS(ept) pj_rwmutex_unlock_read(ept->mod_mutex)
108 #else
109 # define LOCK_MODULE_ACCESS(endpt)
110 # define UNLOCK_MODULE_ACCESS(endpt)
111 #endif
112
113
114
115 /*
116 * Prototypes.
117 */
118 static void endpt_on_rx_msg( pjsip_endpoint*,
119 pj_status_t, pjsip_rx_data*);
120 static pj_status_t endpt_on_tx_msg( pjsip_endpoint *endpt,
121 pjsip_tx_data *tdata );
122 static pj_status_t unload_module(pjsip_endpoint *endpt,
123 pjsip_module *mod);
124
125 /* Defined in sip_parser.c */
126 void init_sip_parser(void);
127 void deinit_sip_parser(void);
128
129 /* Defined in sip_tel_uri.c */
130 pj_status_t pjsip_tel_uri_subsys_init(void);
131
132
133 /*
134 * This is the global handler for memory allocation failure, for pools that
135 * are created by the endpoint (by default, all pools ARE allocated by
136 * endpoint). The error is handled by throwing exception, and hopefully,
137 * the exception will be handled by the application (or this library).
138 */
pool_callback(pj_pool_t * pool,pj_size_t size)139 static void pool_callback( pj_pool_t *pool, pj_size_t size )
140 {
141 PJ_UNUSED_ARG(pool);
142 PJ_UNUSED_ARG(size);
143
144 PJ_THROW(PJSIP_EX_NO_MEMORY);
145 }
146
147
148 /* Compare module name, used for searching module based on name. */
cmp_mod_name(void * name,const void * mod)149 static int cmp_mod_name(void *name, const void *mod)
150 {
151 return pj_stricmp((const pj_str_t*)name, &((pjsip_module*)mod)->name);
152 }
153
154 /*
155 * Register new module to the endpoint.
156 * The endpoint will then call the load and start function in the module to
157 * properly initialize the module, and assign a unique module ID for the
158 * module.
159 */
pjsip_endpt_register_module(pjsip_endpoint * endpt,pjsip_module * mod)160 PJ_DEF(pj_status_t) pjsip_endpt_register_module( pjsip_endpoint *endpt,
161 pjsip_module *mod )
162 {
163 pj_status_t status = PJ_SUCCESS;
164 pjsip_module *m;
165 unsigned i;
166
167 pj_rwmutex_lock_write(endpt->mod_mutex);
168
169 /* Make sure that this module has not been registered. */
170 PJ_ASSERT_ON_FAIL( pj_list_find_node(&endpt->module_list, mod) == NULL,
171 {status = PJ_EEXISTS; goto on_return;});
172
173 /* Make sure that no module with the same name has been registered. */
174 PJ_ASSERT_ON_FAIL( pj_list_search(&endpt->module_list, &mod->name,
175 &cmp_mod_name)==NULL,
176 {status = PJ_EEXISTS; goto on_return; });
177
178 /* Find unused ID for this module. */
179 for (i=0; i<PJ_ARRAY_SIZE(endpt->modules); ++i) {
180 if (endpt->modules[i] == NULL)
181 break;
182 }
183 if (i == PJ_ARRAY_SIZE(endpt->modules)) {
184 pj_assert(!"Too many modules registered!");
185 status = PJ_ETOOMANY;
186 goto on_return;
187 }
188
189 /* Assign the ID. */
190 mod->id = i;
191
192 /* Try to load the module. */
193 if (mod->load) {
194 status = (*mod->load)(endpt);
195 if (status != PJ_SUCCESS)
196 goto on_return;
197 }
198
199 /* Try to start the module. */
200 if (mod->start) {
201 status = (*mod->start)();
202 if (status != PJ_SUCCESS)
203 goto on_return;
204 }
205
206 /* Save the module. */
207 endpt->modules[i] = mod;
208
209 /* Put in the module list, sorted by priority. */
210 m = endpt->module_list.next;
211 while (m != &endpt->module_list) {
212 if (m->priority > mod->priority)
213 break;
214 m = m->next;
215 }
216 pj_list_insert_before(m, mod);
217
218 /* Done. */
219
220 PJ_LOG(4,(THIS_FILE, "Module \"%.*s\" registered",
221 (int)mod->name.slen, mod->name.ptr));
222
223 on_return:
224 pj_rwmutex_unlock_write(endpt->mod_mutex);
225 return status;
226 }
227
228 /*
229 * Unregister a module from the endpoint.
230 * The endpoint will then call the stop and unload function in the module to
231 * properly shutdown the module.
232 */
pjsip_endpt_unregister_module(pjsip_endpoint * endpt,pjsip_module * mod)233 PJ_DEF(pj_status_t) pjsip_endpt_unregister_module( pjsip_endpoint *endpt,
234 pjsip_module *mod )
235 {
236 pj_status_t status;
237
238 pj_rwmutex_lock_write(endpt->mod_mutex);
239
240 /* Make sure the module exists in the list. */
241 PJ_ASSERT_ON_FAIL( pj_list_find_node(&endpt->module_list, mod) == mod,
242 {status = PJ_ENOTFOUND;goto on_return;} );
243
244 /* Make sure the module exists in the array. */
245 PJ_ASSERT_ON_FAIL( mod->id>=0 &&
246 mod->id<(int)PJ_ARRAY_SIZE(endpt->modules) &&
247 endpt->modules[mod->id] == mod,
248 {status = PJ_ENOTFOUND; goto on_return;});
249
250 /* Try to stop the module. */
251 if (mod->stop) {
252 status = (*mod->stop)();
253 if (status != PJ_SUCCESS) goto on_return;
254 }
255
256 /* Unload module */
257 status = unload_module(endpt, mod);
258
259 on_return:
260 pj_rwmutex_unlock_write(endpt->mod_mutex);
261
262 if (status != PJ_SUCCESS) {
263 char errmsg[PJ_ERR_MSG_SIZE];
264
265 pj_strerror(status, errmsg, sizeof(errmsg));
266 PJ_LOG(3,(THIS_FILE, "Module \"%.*s\" can not be unregistered: %s",
267 (int)mod->name.slen, mod->name.ptr, errmsg));
268 }
269
270 return status;
271 }
272
unload_module(pjsip_endpoint * endpt,pjsip_module * mod)273 static pj_status_t unload_module(pjsip_endpoint *endpt,
274 pjsip_module *mod)
275 {
276 pj_status_t status;
277
278 /* Try to unload the module. */
279 if (mod->unload) {
280 status = (*mod->unload)();
281 if (status != PJ_SUCCESS)
282 return status;
283 }
284
285 /* Module MUST NOT set module ID to -1. */
286 pj_assert(mod->id >= 0);
287
288 /* Remove module from array. */
289 endpt->modules[mod->id] = NULL;
290
291 /* Remove module from list. */
292 pj_list_erase(mod);
293
294 /* Set module Id to -1. */
295 mod->id = -1;
296
297 /* Done. */
298 status = PJ_SUCCESS;
299
300 PJ_LOG(4,(THIS_FILE, "Module \"%.*s\" unregistered",
301 (int)mod->name.slen, mod->name.ptr));
302
303 return status;
304 }
305
306
307 /*
308 * Get the value of the specified capability header field.
309 */
pjsip_endpt_get_capability(pjsip_endpoint * endpt,int htype,const pj_str_t * hname)310 PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_capability( pjsip_endpoint *endpt,
311 int htype,
312 const pj_str_t *hname)
313 {
314 pjsip_hdr *hdr = endpt->cap_hdr.next;
315
316 /* Check arguments. */
317 PJ_ASSERT_RETURN(endpt != NULL, NULL);
318 PJ_ASSERT_RETURN(htype != PJSIP_H_OTHER || hname, NULL);
319
320 if (htype != PJSIP_H_OTHER) {
321 while (hdr != &endpt->cap_hdr) {
322 if (hdr->type == htype)
323 return hdr;
324 hdr = hdr->next;
325 }
326 }
327 return NULL;
328 }
329
330
331 /*
332 * Check if the specified capability is supported.
333 */
pjsip_endpt_has_capability(pjsip_endpoint * endpt,int htype,const pj_str_t * hname,const pj_str_t * token)334 PJ_DEF(pj_bool_t) pjsip_endpt_has_capability( pjsip_endpoint *endpt,
335 int htype,
336 const pj_str_t *hname,
337 const pj_str_t *token)
338 {
339 const pjsip_generic_array_hdr *hdr;
340 unsigned i;
341
342 hdr = (const pjsip_generic_array_hdr*)
343 pjsip_endpt_get_capability(endpt, htype, hname);
344 if (!hdr)
345 return PJ_FALSE;
346
347 PJ_ASSERT_RETURN(token != NULL, PJ_FALSE);
348
349 for (i=0; i<hdr->count; ++i) {
350 if (!pj_stricmp(&hdr->values[i], token))
351 return PJ_TRUE;
352 }
353
354 return PJ_FALSE;
355 }
356
357 /*
358 * Add or register new capabilities as indicated by the tags to the
359 * appropriate header fields in the endpoint.
360 */
pjsip_endpt_add_capability(pjsip_endpoint * endpt,pjsip_module * mod,int htype,const pj_str_t * hname,unsigned count,const pj_str_t tags[])361 PJ_DEF(pj_status_t) pjsip_endpt_add_capability( pjsip_endpoint *endpt,
362 pjsip_module *mod,
363 int htype,
364 const pj_str_t *hname,
365 unsigned count,
366 const pj_str_t tags[])
367 {
368 pjsip_generic_array_hdr *hdr;
369 unsigned i;
370
371 PJ_UNUSED_ARG(mod);
372
373 /* Check arguments. */
374 PJ_ASSERT_RETURN(endpt!=NULL && count>0 && tags, PJ_EINVAL);
375 PJ_ASSERT_RETURN(count <= PJSIP_GENERIC_ARRAY_MAX_COUNT, PJ_ETOOMANY);
376 PJ_ASSERT_RETURN(htype==PJSIP_H_ACCEPT ||
377 htype==PJSIP_H_ALLOW ||
378 htype==PJSIP_H_SUPPORTED,
379 PJ_EINVAL);
380
381 /* Find the header. */
382 hdr = (pjsip_generic_array_hdr*) pjsip_endpt_get_capability(endpt,
383 htype, hname);
384
385 /* Create the header when it's not present */
386 if (hdr == NULL) {
387 switch (htype) {
388 case PJSIP_H_ACCEPT:
389 hdr = pjsip_accept_hdr_create(endpt->pool);
390 break;
391 case PJSIP_H_ALLOW:
392 hdr = pjsip_allow_hdr_create(endpt->pool);
393 break;
394 case PJSIP_H_SUPPORTED:
395 hdr = pjsip_supported_hdr_create(endpt->pool);
396 break;
397 default:
398 return PJ_EINVAL;
399 }
400
401 if (hdr) {
402 pj_list_push_back(&endpt->cap_hdr, hdr);
403 }
404 }
405
406 /* Add the tags to the header. */
407 for (i=0; i<count; ++i) {
408 pj_strdup(endpt->pool, &hdr->values[hdr->count], &tags[i]);
409 ++hdr->count;
410 }
411
412 /* Done. */
413 return PJ_SUCCESS;
414 }
415
416 /*
417 * Get additional headers to be put in outgoing request message.
418 */
pjsip_endpt_get_request_headers(pjsip_endpoint * endpt)419 PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt)
420 {
421 return &endpt->req_hdr;
422 }
423
424
425 /*
426 * Initialize endpoint.
427 */
pjsip_endpt_create(pj_pool_factory * pf,const char * name,pjsip_endpoint ** p_endpt)428 PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf,
429 const char *name,
430 pjsip_endpoint **p_endpt)
431 {
432 pj_status_t status;
433 pj_pool_t *pool;
434 pjsip_endpoint *endpt;
435 pjsip_max_fwd_hdr *mf_hdr;
436 pj_lock_t *lock = NULL;
437
438
439 status = pj_register_strerror(PJSIP_ERRNO_START, PJ_ERRNO_SPACE_SIZE,
440 &pjsip_strerror);
441 pj_assert(status == PJ_SUCCESS);
442
443 PJ_LOG(5, (THIS_FILE, "Creating endpoint instance..."));
444
445 *p_endpt = NULL;
446
447 /* Create pool */
448 pool = pj_pool_create(pf, "pept%p",
449 PJSIP_POOL_LEN_ENDPT, PJSIP_POOL_INC_ENDPT,
450 &pool_callback);
451 if (!pool)
452 return PJ_ENOMEM;
453
454 /* Create endpoint. */
455 endpt = PJ_POOL_ZALLOC_T(pool, pjsip_endpoint);
456 endpt->pool = pool;
457 endpt->pf = pf;
458
459 /* Init modules list. */
460 pj_list_init(&endpt->module_list);
461
462 /* Initialize exit callback list. */
463 pj_list_init(&endpt->exit_cb_list);
464
465 /* Create R/W mutex for module manipulation. */
466 status = pj_rwmutex_create(endpt->pool, "ept%p", &endpt->mod_mutex);
467 if (status != PJ_SUCCESS)
468 goto on_error;
469
470 /* Init parser. */
471 init_sip_parser();
472
473 /* Init tel: uri */
474 pjsip_tel_uri_subsys_init();
475
476 /* Get name. */
477 if (name != NULL) {
478 pj_str_t temp;
479 pj_strdup_with_null(endpt->pool, &endpt->name, pj_cstr(&temp, name));
480 } else {
481 pj_strdup_with_null(endpt->pool, &endpt->name, pj_gethostname());
482 }
483
484 /* Create mutex for the events, etc. */
485 status = pj_mutex_create_recursive( endpt->pool, "ept%p", &endpt->mutex );
486 if (status != PJ_SUCCESS) {
487 goto on_error;
488 }
489
490 /* Create timer heap to manage all timers within this endpoint. */
491 status = pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT,
492 &endpt->timer_heap);
493 if (status != PJ_SUCCESS) {
494 goto on_error;
495 }
496
497 /* Set recursive lock for the timer heap. */
498 status = pj_lock_create_recursive_mutex( endpt->pool, "edpt%p", &lock);
499 if (status != PJ_SUCCESS) {
500 goto on_error;
501 }
502 pj_timer_heap_set_lock(endpt->timer_heap, lock, PJ_TRUE);
503
504 /* Set maximum timed out entries to process in a single poll. */
505 pj_timer_heap_set_max_timed_out_per_poll(endpt->timer_heap,
506 PJSIP_MAX_TIMED_OUT_ENTRIES);
507
508 /* Create ioqueue. */
509 status = pj_ioqueue_create( endpt->pool, PJSIP_MAX_TRANSPORTS, &endpt->ioqueue);
510 if (status != PJ_SUCCESS) {
511 goto on_error;
512 }
513
514 /* Create transport manager. */
515 status = pjsip_tpmgr_create( endpt->pool, endpt,
516 &endpt_on_rx_msg,
517 &endpt_on_tx_msg,
518 &endpt->transport_mgr);
519 if (status != PJ_SUCCESS) {
520 goto on_error;
521 }
522
523 /* Create asynchronous DNS resolver. */
524 status = pjsip_resolver_create(endpt->pool, &endpt->resolver);
525 if (status != PJ_SUCCESS) {
526 PJ_PERROR(4, (THIS_FILE, status,
527 "Error creating resolver instance"));
528 goto on_error;
529 }
530
531 /* Initialize request headers. */
532 pj_list_init(&endpt->req_hdr);
533
534 /* Add "Max-Forwards" for request header. */
535 mf_hdr = pjsip_max_fwd_hdr_create(endpt->pool,
536 PJSIP_MAX_FORWARDS_VALUE);
537 pj_list_insert_before( &endpt->req_hdr, mf_hdr);
538
539 /* Initialize capability header list. */
540 pj_list_init(&endpt->cap_hdr);
541
542
543 /* Done. */
544 *p_endpt = endpt;
545 return status;
546
547 on_error:
548 if (endpt->transport_mgr) {
549 pjsip_tpmgr_destroy(endpt->transport_mgr);
550 endpt->transport_mgr = NULL;
551 }
552 if (endpt->ioqueue) {
553 pj_ioqueue_destroy(endpt->ioqueue);
554 endpt->ioqueue = NULL;
555 }
556 if (endpt->timer_heap) {
557 pj_timer_heap_destroy(endpt->timer_heap);
558 endpt->timer_heap = NULL;
559 }
560 if (endpt->mutex) {
561 pj_mutex_destroy(endpt->mutex);
562 endpt->mutex = NULL;
563 }
564 deinit_sip_parser();
565 if (endpt->mod_mutex) {
566 pj_rwmutex_destroy(endpt->mod_mutex);
567 endpt->mod_mutex = NULL;
568 }
569 pj_pool_release( endpt->pool );
570
571 PJ_PERROR(4, (THIS_FILE, status, "Error creating endpoint"));
572 return status;
573 }
574
575 /*
576 * Destroy endpoint.
577 */
pjsip_endpt_destroy(pjsip_endpoint * endpt)578 PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt)
579 {
580 pjsip_module *mod;
581 exit_cb *ecb;
582
583 PJ_LOG(5, (THIS_FILE, "Destroying endpoint instance.."));
584
585 /* Phase 1: stop all modules */
586 mod = endpt->module_list.prev;
587 while (mod != &endpt->module_list) {
588 pjsip_module *prev = mod->prev;
589 if (mod->stop) {
590 (*mod->stop)();
591 }
592 mod = prev;
593 }
594
595 /* Phase 2: unload modules. */
596 mod = endpt->module_list.prev;
597 while (mod != &endpt->module_list) {
598 pjsip_module *prev = mod->prev;
599 unload_module(endpt, mod);
600 mod = prev;
601 }
602
603 /* Destroy resolver */
604 pjsip_resolver_destroy(endpt->resolver);
605
606 /* Shutdown and destroy all transports. */
607 pjsip_tpmgr_destroy(endpt->transport_mgr);
608
609 /* Destroy ioqueue */
610 pj_ioqueue_destroy(endpt->ioqueue);
611
612 /* Destroy timer heap */
613 #if PJ_TIMER_DEBUG
614 pj_timer_heap_dump(endpt->timer_heap);
615 #endif
616 pj_timer_heap_destroy(endpt->timer_heap);
617
618 /* Call all registered exit callbacks */
619 ecb = endpt->exit_cb_list.next;
620 while (ecb != &endpt->exit_cb_list) {
621 (*ecb->func)(endpt);
622 ecb = ecb->next;
623 }
624
625 /* Delete endpoint mutex. */
626 pj_mutex_destroy(endpt->mutex);
627
628 /* Deinit parser */
629 deinit_sip_parser();
630
631 /* Delete module's mutex */
632 pj_rwmutex_destroy(endpt->mod_mutex);
633
634 /* Finally destroy pool. */
635 pj_pool_release(endpt->pool);
636
637 PJ_LOG(4, (THIS_FILE, "Endpoint %p destroyed", endpt));
638 }
639
640 /*
641 * Get endpoint name.
642 */
pjsip_endpt_name(const pjsip_endpoint * endpt)643 PJ_DEF(const pj_str_t*) pjsip_endpt_name(const pjsip_endpoint *endpt)
644 {
645 return &endpt->name;
646 }
647
648
649 /*
650 * Create new pool.
651 */
pjsip_endpt_create_pool(pjsip_endpoint * endpt,const char * pool_name,pj_size_t initial,pj_size_t increment)652 PJ_DEF(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt,
653 const char *pool_name,
654 pj_size_t initial,
655 pj_size_t increment )
656 {
657 pj_pool_t *pool;
658
659 /* Lock endpoint mutex. */
660 /* No need to lock mutex. Factory is thread safe.
661 pj_mutex_lock(endpt->mutex);
662 */
663
664 /* Create pool */
665 pool = pj_pool_create( endpt->pf, pool_name,
666 initial, increment, &pool_callback);
667
668 /* Unlock mutex. */
669 /* No need to lock mutex. Factory is thread safe.
670 pj_mutex_unlock(endpt->mutex);
671 */
672
673 if (!pool) {
674 PJ_LOG(4, (THIS_FILE, "Unable to create pool %s!", pool_name));
675 }
676
677 return pool;
678 }
679
680 /*
681 * Return back pool to endpoint's pool manager to be either destroyed or
682 * recycled.
683 */
pjsip_endpt_release_pool(pjsip_endpoint * endpt,pj_pool_t * pool)684 PJ_DEF(void) pjsip_endpt_release_pool( pjsip_endpoint *endpt, pj_pool_t *pool )
685 {
686 PJ_LOG(6, (THIS_FILE, "Releasing pool %s", pj_pool_getobjname(pool)));
687
688 /* Don't need to acquire mutex since pool factory is thread safe
689 pj_mutex_lock(endpt->mutex);
690 */
691 pj_pool_release( pool );
692
693 PJ_UNUSED_ARG(endpt);
694 /*
695 pj_mutex_unlock(endpt->mutex);
696 */
697 }
698
699
pjsip_endpt_handle_events2(pjsip_endpoint * endpt,const pj_time_val * max_timeout,unsigned * p_count)700 PJ_DEF(pj_status_t) pjsip_endpt_handle_events2(pjsip_endpoint *endpt,
701 const pj_time_val *max_timeout,
702 unsigned *p_count)
703 {
704 enum { MAX_TIMEOUT_ON_ERR = 10 };
705 /* timeout is 'out' var. This just to make compiler happy. */
706 pj_time_val timeout = { 0, 0};
707 unsigned count = 0, net_event_count = 0;
708 int c;
709
710 PJ_LOG(6, (THIS_FILE, "pjsip_endpt_handle_events()"));
711
712 /* Poll the timer. The timer heap has its own mutex for better
713 * granularity, so we don't need to lock end endpoint.
714 */
715 timeout.sec = timeout.msec = 0;
716 c = pj_timer_heap_poll( endpt->timer_heap, &timeout );
717 if (c > 0)
718 count += c;
719
720 /* timer_heap_poll should never ever returns negative value, or otherwise
721 * ioqueue_poll() will block forever!
722 */
723 pj_assert(timeout.sec >= 0 && timeout.msec >= 0);
724 if (timeout.msec >= 1000) timeout.msec = 999;
725
726 /* If caller specifies maximum time to wait, then compare the value with
727 * the timeout to wait from timer, and use the minimum value.
728 */
729 if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) {
730 timeout = *max_timeout;
731 }
732
733 /* Poll ioqueue.
734 * Repeat polling the ioqueue while we have immediate events, because
735 * timer heap may process more than one events, so if we only process
736 * one network events at a time (such as when IOCP backend is used),
737 * the ioqueue may have trouble keeping up with the request rate.
738 *
739 * For example, for each send() request, one network event will be
740 * reported by ioqueue for the send() completion. If we don't poll
741 * the ioqueue often enough, the send() completion will not be
742 * reported in timely manner.
743 */
744 do {
745 c = pj_ioqueue_poll( endpt->ioqueue, &timeout);
746 if (c < 0) {
747 pj_status_t err = pj_get_netos_error();
748 #if PJSIP_HANDLE_EVENTS_HAS_SLEEP_ON_ERR
749 unsigned msec = PJ_TIME_VAL_MSEC(timeout);
750 pj_thread_sleep(PJ_MIN(msec, MAX_TIMEOUT_ON_ERR));
751 #endif
752
753 if (p_count)
754 *p_count = count;
755 return err;
756 } else if (c == 0) {
757 break;
758 } else {
759 net_event_count += c;
760 timeout.sec = timeout.msec = 0;
761 }
762 } while (c > 0 && net_event_count < PJSIP_MAX_NET_EVENTS);
763
764 count += net_event_count;
765 if (p_count)
766 *p_count = count;
767
768 return PJ_SUCCESS;
769 }
770
771 /*
772 * Handle events.
773 */
pjsip_endpt_handle_events(pjsip_endpoint * endpt,const pj_time_val * max_timeout)774 PJ_DEF(pj_status_t) pjsip_endpt_handle_events(pjsip_endpoint *endpt,
775 const pj_time_val *max_timeout)
776 {
777 return pjsip_endpt_handle_events2(endpt, max_timeout, NULL);
778 }
779
780 /*
781 * Schedule timer.
782 */
783 #if PJ_TIMER_DEBUG
pjsip_endpt_schedule_timer_dbg(pjsip_endpoint * endpt,pj_timer_entry * entry,const pj_time_val * delay,const char * src_file,int src_line)784 PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer_dbg(pjsip_endpoint *endpt,
785 pj_timer_entry *entry,
786 const pj_time_val *delay,
787 const char *src_file,
788 int src_line)
789 {
790 PJ_LOG(6, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)",
791 entry, delay->sec, delay->msec));
792 return pj_timer_heap_schedule_dbg(endpt->timer_heap, entry, delay,
793 src_file, src_line);
794 }
795 #else
pjsip_endpt_schedule_timer(pjsip_endpoint * endpt,pj_timer_entry * entry,const pj_time_val * delay)796 PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
797 pj_timer_entry *entry,
798 const pj_time_val *delay )
799 {
800 PJ_LOG(6, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)",
801 entry, delay->sec, delay->msec));
802 return pj_timer_heap_schedule( endpt->timer_heap, entry, delay );
803 }
804 #endif
805
806 /*
807 * Schedule timer with group lock.
808 */
809 #if PJ_TIMER_DEBUG
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)810 PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer_w_grp_lock_dbg(
811 pjsip_endpoint *endpt,
812 pj_timer_entry *entry,
813 const pj_time_val *delay,
814 int id_val,
815 pj_grp_lock_t *grp_lock,
816 const char *src_file,
817 int src_line)
818 {
819 PJ_LOG(6, (THIS_FILE, "pjsip_endpt_schedule_timer_w_grp_lock"
820 "(entry=%p, delay=%u.%u, grp_lock=%p)",
821 entry, delay->sec, delay->msec, grp_lock));
822 return pj_timer_heap_schedule_w_grp_lock_dbg(endpt->timer_heap, entry,
823 delay, id_val, grp_lock,
824 src_file, src_line);
825 }
826 #else
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)827 PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer_w_grp_lock(
828 pjsip_endpoint *endpt,
829 pj_timer_entry *entry,
830 const pj_time_val *delay,
831 int id_val,
832 pj_grp_lock_t *grp_lock )
833 {
834 PJ_LOG(6, (THIS_FILE, "pjsip_endpt_schedule_timer_w_grp_lock"
835 "(entry=%p, delay=%u.%u, grp_lock=%p)",
836 entry, delay->sec, delay->msec, grp_lock));
837 return pj_timer_heap_schedule_w_grp_lock( endpt->timer_heap, entry,
838 delay, id_val, grp_lock );
839 }
840 #endif
841
842 /*
843 * Cancel the previously registered timer.
844 */
pjsip_endpt_cancel_timer(pjsip_endpoint * endpt,pj_timer_entry * entry)845 PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt,
846 pj_timer_entry *entry )
847 {
848 PJ_LOG(6, (THIS_FILE, "pjsip_endpt_cancel_timer(entry=%p)", entry));
849 pj_timer_heap_cancel( endpt->timer_heap, entry );
850 }
851
852 /*
853 * Get the timer heap instance of the SIP endpoint.
854 */
pjsip_endpt_get_timer_heap(pjsip_endpoint * endpt)855 PJ_DEF(pj_timer_heap_t*) pjsip_endpt_get_timer_heap(pjsip_endpoint *endpt)
856 {
857 return endpt->timer_heap;
858 }
859
860 /* Init with default */
pjsip_process_rdata_param_default(pjsip_process_rdata_param * p)861 PJ_DEF(void) pjsip_process_rdata_param_default(pjsip_process_rdata_param *p)
862 {
863 pj_bzero(p, sizeof(*p));
864 }
865
866 /* Distribute rdata */
pjsip_endpt_process_rx_data(pjsip_endpoint * endpt,pjsip_rx_data * rdata,pjsip_process_rdata_param * p,pj_bool_t * p_handled)867 PJ_DEF(pj_status_t) pjsip_endpt_process_rx_data( pjsip_endpoint *endpt,
868 pjsip_rx_data *rdata,
869 pjsip_process_rdata_param *p,
870 pj_bool_t *p_handled)
871 {
872 pjsip_msg *msg;
873 pjsip_process_rdata_param def_prm;
874 pjsip_module *mod;
875 pj_bool_t handled = PJ_FALSE;
876 unsigned i;
877 pj_status_t status;
878
879 PJ_ASSERT_RETURN(endpt && rdata, PJ_EINVAL);
880
881 if (p==NULL) {
882 p = &def_prm;
883 pjsip_process_rdata_param_default(p);
884 }
885
886 msg = rdata->msg_info.msg;
887
888 if (p_handled)
889 *p_handled = PJ_FALSE;
890
891 if (!p->silent) {
892 PJ_LOG(5, (THIS_FILE, "Distributing rdata to modules: %s",
893 pjsip_rx_data_get_info(rdata)));
894 pj_log_push_indent();
895 }
896
897 LOCK_MODULE_ACCESS(endpt);
898
899 /* Find start module */
900 if (p->start_mod) {
901 mod = (pjsip_module*)
902 pj_list_find_node(&endpt->module_list, p->start_mod);
903 if (!mod) {
904 status = PJ_ENOTFOUND;
905 goto on_return;
906 }
907 } else {
908 mod = endpt->module_list.next;
909 }
910
911 /* Start after the specified index */
912 for (i=0; i < p->idx_after_start && mod != &endpt->module_list; ++i) {
913 mod = mod->next;
914 }
915
916 /* Start with the specified priority */
917 while (mod != &endpt->module_list && mod->priority < (int)p->start_prio) {
918 mod = mod->next;
919 }
920
921 if (mod == &endpt->module_list) {
922 status = PJ_ENOTFOUND;
923 goto on_return;
924 }
925
926 /* Distribute */
927 if (msg->type == PJSIP_REQUEST_MSG) {
928 do {
929 if (mod->on_rx_request)
930 handled = (*mod->on_rx_request)(rdata);
931 if (handled)
932 break;
933 mod = mod->next;
934 } while (mod != &endpt->module_list);
935 } else {
936 do {
937 if (mod->on_rx_response)
938 handled = (*mod->on_rx_response)(rdata);
939 if (handled)
940 break;
941 mod = mod->next;
942 } while (mod != &endpt->module_list);
943 }
944
945 status = PJ_SUCCESS;
946
947 on_return:
948 if (p_handled)
949 *p_handled = handled;
950
951 UNLOCK_MODULE_ACCESS(endpt);
952 if (!p->silent) {
953 pj_log_pop_indent();
954 }
955 return status;
956 }
957
958 /*
959 * This is the callback that is called by the transport manager when it
960 * receives a message from the network.
961 */
endpt_on_rx_msg(pjsip_endpoint * endpt,pj_status_t status,pjsip_rx_data * rdata)962 static void endpt_on_rx_msg( pjsip_endpoint *endpt,
963 pj_status_t status,
964 pjsip_rx_data *rdata )
965 {
966 pjsip_msg *msg = rdata->msg_info.msg;
967 pjsip_process_rdata_param proc_prm;
968 pj_bool_t handled = PJ_FALSE;
969
970 PJ_UNUSED_ARG(msg);
971
972 if (status != PJ_SUCCESS) {
973 char info[30];
974 char errmsg[PJ_ERR_MSG_SIZE];
975
976 info[0] = '\0';
977
978 if (status == PJSIP_EMISSINGHDR) {
979 pj_str_t p;
980
981 p.ptr = info; p.slen = 0;
982
983 if (rdata->msg_info.cid == NULL || rdata->msg_info.cid->id.slen)
984 pj_strcpy2(&p, "Call-ID");
985 if (rdata->msg_info.from == NULL)
986 pj_strcpy2(&p, " From");
987 if (rdata->msg_info.to == NULL)
988 pj_strcpy2(&p, " To");
989 if (rdata->msg_info.via == NULL)
990 pj_strcpy2(&p, " Via");
991 if (rdata->msg_info.cseq == NULL)
992 pj_strcpy2(&p, " CSeq");
993
994 p.ptr[p.slen] = '\0';
995 }
996
997 pj_strerror(status, errmsg, sizeof(errmsg));
998
999 PJ_LOG(1, (THIS_FILE,
1000 "Error processing packet from %s:%d: %s %s [code %d]:\n"
1001 "%.*s\n"
1002 "-- end of packet.",
1003 rdata->pkt_info.src_name,
1004 rdata->pkt_info.src_port,
1005 errmsg,
1006 info,
1007 status,
1008 (int)rdata->msg_info.len,
1009 rdata->msg_info.msg_buf));
1010 return;
1011 }
1012
1013 PJ_LOG(5, (THIS_FILE, "Processing incoming message: %s",
1014 pjsip_rx_data_get_info(rdata)));
1015 pj_log_push_indent();
1016
1017 #if defined(PJSIP_CHECK_VIA_SENT_BY) && PJSIP_CHECK_VIA_SENT_BY != 0
1018 /* For response, check that the value in Via sent-by match the transport.
1019 * If not matched, silently drop the response.
1020 * Ref: RFC3261 Section 18.1.2 Receiving Response
1021 */
1022 if (msg->type == PJSIP_RESPONSE_MSG) {
1023 const pj_str_t *local_addr;
1024 int port = rdata->msg_info.via->sent_by.port;
1025 pj_bool_t mismatch = PJ_FALSE;
1026 if (port == 0) {
1027 pjsip_transport_type_e type;
1028 type = (pjsip_transport_type_e)rdata->tp_info.transport->key.type;
1029 port = pjsip_transport_get_default_port_for_type(type);
1030 }
1031 local_addr = &rdata->tp_info.transport->local_name.host;
1032
1033 if (pj_strcmp(&rdata->msg_info.via->sent_by.host, local_addr) != 0) {
1034
1035 /* The RFC says that we should drop response when sent-by
1036 * address mismatch. But it could happen (e.g. with SER) when
1037 * endpoint with private IP is sending request to public
1038 * server.
1039
1040 mismatch = PJ_TRUE;
1041
1042 */
1043
1044 } else if (port != rdata->tp_info.transport->local_name.port) {
1045 /* Port or address mismatch, we should discard response */
1046 /* But we saw one implementation (we don't want to name it to
1047 * protect the innocence) which put wrong sent-by port although
1048 * the "rport" parameter is correct.
1049 * So we discard the response only if the port doesn't match
1050 * both the port in sent-by and rport. We try to be lenient here!
1051 */
1052 if (rdata->msg_info.via->rport_param !=
1053 rdata->tp_info.transport->local_name.port)
1054 mismatch = PJ_TRUE;
1055 else {
1056 PJ_LOG(4,(THIS_FILE, "Message %s from %s has mismatch port in "
1057 "sent-by but the rport parameter is "
1058 "correct",
1059 pjsip_rx_data_get_info(rdata),
1060 rdata->pkt_info.src_name));
1061 }
1062 }
1063
1064 if (mismatch) {
1065 PJ_TODO(ENDPT_REPORT_WHEN_DROPPING_MESSAGE);
1066 PJ_LOG(4,(THIS_FILE, "Dropping response %s from %s:%d because "
1067 "sent-by is mismatch",
1068 pjsip_rx_data_get_info(rdata),
1069 rdata->pkt_info.src_name,
1070 rdata->pkt_info.src_port));
1071 pj_log_pop_indent();
1072 return;
1073 }
1074 }
1075 #endif
1076
1077 pjsip_process_rdata_param_default(&proc_prm);
1078 proc_prm.silent = PJ_TRUE;
1079
1080 pjsip_endpt_process_rx_data(endpt, rdata, &proc_prm, &handled);
1081
1082 /* No module is able to handle the message */
1083 if (!handled) {
1084 PJ_LOG(4,(THIS_FILE, "%s from %s:%d was dropped/unhandled by"
1085 " any modules",
1086 pjsip_rx_data_get_info(rdata),
1087 rdata->pkt_info.src_name,
1088 rdata->pkt_info.src_port));
1089 }
1090
1091 /* Must clear mod_data before returning rdata to transport, since
1092 * rdata may be reused.
1093 */
1094 pj_bzero(&rdata->endpt_info, sizeof(rdata->endpt_info));
1095
1096 pj_log_pop_indent();
1097 }
1098
1099 /*
1100 * This callback is called by transport manager before message is sent.
1101 * Modules may inspect the message before it's actually sent.
1102 */
endpt_on_tx_msg(pjsip_endpoint * endpt,pjsip_tx_data * tdata)1103 static pj_status_t endpt_on_tx_msg( pjsip_endpoint *endpt,
1104 pjsip_tx_data *tdata )
1105 {
1106 pj_status_t status = PJ_SUCCESS;
1107 pjsip_module *mod;
1108
1109 /* Distribute to modules, starting from modules with LOWEST priority */
1110 LOCK_MODULE_ACCESS(endpt);
1111
1112 mod = endpt->module_list.prev;
1113 if (tdata->msg->type == PJSIP_REQUEST_MSG) {
1114 while (mod != &endpt->module_list) {
1115 if (mod->on_tx_request)
1116 status = (*mod->on_tx_request)(tdata);
1117 if (status != PJ_SUCCESS)
1118 break;
1119 mod = mod->prev;
1120 }
1121
1122 } else {
1123 while (mod != &endpt->module_list) {
1124 if (mod->on_tx_response)
1125 status = (*mod->on_tx_response)(tdata);
1126 if (status != PJ_SUCCESS)
1127 break;
1128 mod = mod->prev;
1129 }
1130 }
1131
1132 UNLOCK_MODULE_ACCESS(endpt);
1133
1134 return status;
1135 }
1136
1137
1138 /*
1139 * Create transmit data buffer.
1140 */
pjsip_endpt_create_tdata(pjsip_endpoint * endpt,pjsip_tx_data ** p_tdata)1141 PJ_DEF(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt,
1142 pjsip_tx_data **p_tdata)
1143 {
1144 return pjsip_tx_data_create(endpt->transport_mgr, p_tdata);
1145 }
1146
1147 /*
1148 * Create the DNS resolver instance.
1149 */
pjsip_endpt_create_resolver(pjsip_endpoint * endpt,pj_dns_resolver ** p_resv)1150 PJ_DEF(pj_status_t) pjsip_endpt_create_resolver(pjsip_endpoint *endpt,
1151 pj_dns_resolver **p_resv)
1152 {
1153 #if PJSIP_HAS_RESOLVER
1154 PJ_ASSERT_RETURN(endpt && p_resv, PJ_EINVAL);
1155 return pj_dns_resolver_create( endpt->pf, NULL, 0, endpt->timer_heap,
1156 endpt->ioqueue, p_resv);
1157 #else
1158 PJ_UNUSED_ARG(endpt);
1159 PJ_UNUSED_ARG(p_resv);
1160 pj_assert(!"Resolver is disabled (PJSIP_HAS_RESOLVER==0)");
1161 return PJ_EINVALIDOP;
1162 #endif
1163 }
1164
1165 /*
1166 * Set DNS resolver to be used by the SIP resolver.
1167 */
pjsip_endpt_set_resolver(pjsip_endpoint * endpt,pj_dns_resolver * resv)1168 PJ_DEF(pj_status_t) pjsip_endpt_set_resolver( pjsip_endpoint *endpt,
1169 pj_dns_resolver *resv)
1170 {
1171 return pjsip_resolver_set_resolver(endpt->resolver, resv);
1172 }
1173
1174 /*
1175 * Set DNS external resolver implementation to be used by the SIP resolver.
1176 */
pjsip_endpt_set_ext_resolver(pjsip_endpoint * endpt,pjsip_ext_resolver * ext_res)1177 PJ_DEF(pj_status_t) pjsip_endpt_set_ext_resolver(pjsip_endpoint *endpt,
1178 pjsip_ext_resolver *ext_res)
1179 {
1180 return pjsip_resolver_set_ext_resolver(endpt->resolver, ext_res);
1181 }
1182
1183 /*
1184 * Get the DNS resolver being used by the SIP resolver.
1185 */
pjsip_endpt_get_resolver(pjsip_endpoint * endpt)1186 PJ_DEF(pj_dns_resolver*) pjsip_endpt_get_resolver(pjsip_endpoint *endpt)
1187 {
1188 PJ_ASSERT_RETURN(endpt, NULL);
1189 return pjsip_resolver_get_resolver(endpt->resolver);
1190 }
1191
1192 /*
1193 * Resolve
1194 */
pjsip_endpt_resolve(pjsip_endpoint * endpt,pj_pool_t * pool,pjsip_host_info * target,void * token,pjsip_resolver_callback * cb)1195 PJ_DEF(void) pjsip_endpt_resolve( pjsip_endpoint *endpt,
1196 pj_pool_t *pool,
1197 pjsip_host_info *target,
1198 void *token,
1199 pjsip_resolver_callback *cb)
1200 {
1201 pjsip_resolve( endpt->resolver, pool, target, token, cb);
1202 }
1203
1204 /*
1205 * Get transport manager.
1206 */
pjsip_endpt_get_tpmgr(pjsip_endpoint * endpt)1207 PJ_DEF(pjsip_tpmgr*) pjsip_endpt_get_tpmgr(pjsip_endpoint *endpt)
1208 {
1209 return endpt->transport_mgr;
1210 }
1211
1212 /*
1213 * Get ioqueue instance.
1214 */
pjsip_endpt_get_ioqueue(pjsip_endpoint * endpt)1215 PJ_DEF(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt)
1216 {
1217 return endpt->ioqueue;
1218 }
1219
1220 /*
1221 * Find/create transport.
1222 */
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)1223 PJ_DEF(pj_status_t) pjsip_endpt_acquire_transport(pjsip_endpoint *endpt,
1224 pjsip_transport_type_e type,
1225 const pj_sockaddr_t *remote,
1226 int addr_len,
1227 const pjsip_tpselector *sel,
1228 pjsip_transport **transport)
1229 {
1230 return pjsip_tpmgr_acquire_transport(endpt->transport_mgr, type,
1231 remote, addr_len, sel, transport);
1232 }
1233
1234
1235 /*
1236 * Find/create transport.
1237 */
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)1238 PJ_DEF(pj_status_t) pjsip_endpt_acquire_transport2(pjsip_endpoint *endpt,
1239 pjsip_transport_type_e type,
1240 const pj_sockaddr_t *remote,
1241 int addr_len,
1242 const pjsip_tpselector *sel,
1243 pjsip_tx_data *tdata,
1244 pjsip_transport **transport)
1245 {
1246 return pjsip_tpmgr_acquire_transport2(endpt->transport_mgr, type, remote,
1247 addr_len, sel, tdata, transport);
1248 }
1249
1250
1251 /*
1252 * Report error.
1253 */
pjsip_endpt_log_error(pjsip_endpoint * endpt,const char * sender,pj_status_t error_code,const char * format,...)1254 PJ_DEF(void) pjsip_endpt_log_error( pjsip_endpoint *endpt,
1255 const char *sender,
1256 pj_status_t error_code,
1257 const char *format,
1258 ... )
1259 {
1260 #if PJ_LOG_MAX_LEVEL > 0
1261 char newformat[256];
1262 pj_size_t len;
1263 va_list marker;
1264
1265 va_start(marker, format);
1266
1267 PJ_UNUSED_ARG(endpt);
1268
1269 len = pj_ansi_strlen(format);
1270 if (len < (int)sizeof(newformat)-30) {
1271 pj_str_t errstr;
1272
1273 pj_ansi_strcpy(newformat, format);
1274 pj_ansi_snprintf(newformat+len, sizeof(newformat)-len-1,
1275 ": [err %d] ", error_code);
1276 len += pj_ansi_strlen(newformat+len);
1277
1278 errstr = pj_strerror( error_code, newformat+len,
1279 sizeof(newformat)-len-1);
1280
1281 len += errstr.slen;
1282 newformat[len] = '\0';
1283
1284 pj_log(sender, 1, newformat, marker);
1285 } else {
1286 pj_log(sender, 1, format, marker);
1287 }
1288
1289 va_end(marker);
1290 #else
1291 PJ_UNUSED_ARG(format);
1292 PJ_UNUSED_ARG(error_code);
1293 PJ_UNUSED_ARG(sender);
1294 PJ_UNUSED_ARG(endpt);
1295 #endif
1296 }
1297
1298
1299 /*
1300 * Dump endpoint.
1301 */
pjsip_endpt_dump(pjsip_endpoint * endpt,pj_bool_t detail)1302 PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail )
1303 {
1304 #if PJ_LOG_MAX_LEVEL >= 3
1305 PJ_LOG(5, (THIS_FILE, "pjsip_endpt_dump()"));
1306
1307 /* Lock mutex. */
1308 pj_mutex_lock(endpt->mutex);
1309
1310 PJ_LOG(3, (THIS_FILE, "Dumping endpoint %p:", endpt));
1311
1312 /* Dumping pool factory. */
1313 pj_pool_factory_dump(endpt->pf, detail);
1314
1315 /* Pool health. */
1316 PJ_LOG(3, (THIS_FILE," Endpoint pool capacity=%u, used_size=%u",
1317 pj_pool_get_capacity(endpt->pool),
1318 pj_pool_get_used_size(endpt->pool)));
1319
1320 /* Resolver */
1321 #if PJSIP_HAS_RESOLVER
1322 if (pjsip_endpt_get_resolver(endpt)) {
1323 pj_dns_resolver_dump(pjsip_endpt_get_resolver(endpt), detail);
1324 }
1325 #endif
1326
1327 /* Transports.
1328 */
1329 pjsip_tpmgr_dump_transports( endpt->transport_mgr );
1330
1331 /* Timer. */
1332 #if PJ_TIMER_DEBUG
1333 pj_timer_heap_dump(endpt->timer_heap);
1334 #else
1335 PJ_LOG(3,(THIS_FILE, " Timer heap has %u entries",
1336 pj_timer_heap_count(endpt->timer_heap)));
1337 #endif
1338
1339 /* Unlock mutex. */
1340 pj_mutex_unlock(endpt->mutex);
1341 #else
1342 PJ_UNUSED_ARG(endpt);
1343 PJ_UNUSED_ARG(detail);
1344 PJ_LOG(3,(THIS_FILE, "pjsip_end_dump: can't dump because it's disabled."));
1345 #endif
1346 }
1347
1348
pjsip_endpt_atexit(pjsip_endpoint * endpt,pjsip_endpt_exit_callback func)1349 PJ_DEF(pj_status_t) pjsip_endpt_atexit( pjsip_endpoint *endpt,
1350 pjsip_endpt_exit_callback func)
1351 {
1352 exit_cb *new_cb;
1353
1354 PJ_ASSERT_RETURN(endpt && func, PJ_EINVAL);
1355
1356 new_cb = PJ_POOL_ZALLOC_T(endpt->pool, exit_cb);
1357 new_cb->func = func;
1358
1359 pj_mutex_lock(endpt->mutex);
1360 pj_list_push_back(&endpt->exit_cb_list, new_cb);
1361 pj_mutex_unlock(endpt->mutex);
1362
1363 return PJ_SUCCESS;
1364 }
1365