blob: 95ad7aa8f5aa70eacd2eb6127652310154660e78 [file] [log] [blame]
Benny Prijono268ca612006-02-07 12:34:11 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijono268ca612006-02-07 12:34:11 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000020#include <pjsua-lib/pjsua_internal.h>
Benny Prijono268ca612006-02-07 12:34:11 +000021
Benny Prijono268ca612006-02-07 12:34:11 +000022
Benny Prijono84126ab2006-02-09 09:30:09 +000023#define THIS_FILE "pjsua_core.c"
Benny Prijono268ca612006-02-07 12:34:11 +000024
25
Benny Prijonoeebe9af2006-06-13 22:57:13 +000026/* PJSUA application instance. */
27struct pjsua_data pjsua_var;
Benny Prijono84126ab2006-02-09 09:30:09 +000028
29
Benny Prijonoeebe9af2006-06-13 22:57:13 +000030/* Display error */
31PJ_DEF(void) pjsua_perror( const char *sender, const char *title,
32 pj_status_t status)
Benny Prijono268ca612006-02-07 12:34:11 +000033{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000034 char errmsg[PJ_ERR_MSG_SIZE];
Benny Prijonoa91a0032006-02-26 21:23:45 +000035
Benny Prijonoeebe9af2006-06-13 22:57:13 +000036 pj_strerror(status, errmsg, sizeof(errmsg));
37 PJ_LOG(3,(sender, "%s: %s [status=%d]", title, errmsg, status));
Benny Prijono268ca612006-02-07 12:34:11 +000038}
39
40
Benny Prijonoeebe9af2006-06-13 22:57:13 +000041static void init_data()
Benny Prijonodc39fe82006-05-26 12:17:46 +000042{
43 unsigned i;
44
Benny Prijonoc97608e2007-03-23 16:34:20 +000045 pj_bzero(&pjsua_var, sizeof(pjsua_var));
46
Benny Prijonoeebe9af2006-06-13 22:57:13 +000047 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i)
48 pjsua_var.acc[i].index = i;
49
50 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i)
51 pjsua_var.tpdata[i].index = i;
Benny Prijonoc97608e2007-03-23 16:34:20 +000052
53 pjsua_var.stun_status = PJ_EUNKNOWN;
Benny Prijono6ba8c542007-10-16 01:34:14 +000054 pjsua_var.nat_status = PJ_EPENDING;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000055}
Benny Prijonodc39fe82006-05-26 12:17:46 +000056
57
Benny Prijono1f61a8f2007-08-16 10:11:44 +000058PJ_DEF(void) pjsua_logging_config_default(pjsua_logging_config *cfg)
59{
60 pj_bzero(cfg, sizeof(*cfg));
61
62 cfg->msg_logging = PJ_TRUE;
63 cfg->level = 5;
64 cfg->console_level = 4;
65 cfg->decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME |
66 PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE;
67}
68
69PJ_DEF(void) pjsua_logging_config_dup(pj_pool_t *pool,
70 pjsua_logging_config *dst,
71 const pjsua_logging_config *src)
72{
73 pj_memcpy(dst, src, sizeof(*src));
74 pj_strdup_with_null(pool, &dst->log_filename, &src->log_filename);
75}
76
77PJ_DEF(void) pjsua_config_default(pjsua_config *cfg)
78{
79 pj_bzero(cfg, sizeof(*cfg));
80
81 cfg->max_calls = 4;
82 cfg->thread_cnt = 1;
Benny Prijono6ba8c542007-10-16 01:34:14 +000083 cfg->nat_type_in_sdp = 2;
Benny Prijono1f61a8f2007-08-16 10:11:44 +000084}
85
Benny Prijono1f61a8f2007-08-16 10:11:44 +000086PJ_DEF(void) pjsua_config_dup(pj_pool_t *pool,
87 pjsua_config *dst,
88 const pjsua_config *src)
89{
90 unsigned i;
91
92 pj_memcpy(dst, src, sizeof(*src));
93
94 for (i=0; i<src->outbound_proxy_cnt; ++i) {
95 pj_strdup_with_null(pool, &dst->outbound_proxy[i],
96 &src->outbound_proxy[i]);
97 }
98
99 for (i=0; i<src->cred_count; ++i) {
100 pjsip_cred_dup(pool, &dst->cred_info[i], &src->cred_info[i]);
101 }
102
103 pj_strdup_with_null(pool, &dst->user_agent, &src->user_agent);
104 pj_strdup_with_null(pool, &dst->stun_domain, &src->stun_domain);
105 pj_strdup_with_null(pool, &dst->stun_host, &src->stun_host);
106 pj_strdup_with_null(pool, &dst->stun_relay_host, &src->stun_relay_host);
107}
108
109PJ_DEF(void) pjsua_msg_data_init(pjsua_msg_data *msg_data)
110{
111 pj_bzero(msg_data, sizeof(*msg_data));
112 pj_list_init(&msg_data->hdr_list);
113}
114
115PJ_DEF(void) pjsua_transport_config_default(pjsua_transport_config *cfg)
116{
117 pj_bzero(cfg, sizeof(*cfg));
118 pjsip_tls_setting_default(&cfg->tls_setting);
119}
120
121PJ_DEF(void) pjsua_transport_config_dup(pj_pool_t *pool,
122 pjsua_transport_config *dst,
123 const pjsua_transport_config *src)
124{
125 PJ_UNUSED_ARG(pool);
126 pj_memcpy(dst, src, sizeof(*src));
127}
128
129PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg)
130{
131 pj_bzero(cfg, sizeof(*cfg));
132
133 cfg->reg_timeout = PJSUA_REG_INTERVAL;
134 cfg->transport_id = PJSUA_INVALID_ID;
Benny Prijono15b02302007-09-27 14:07:07 +0000135 cfg->auto_update_nat = PJ_TRUE;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000136 cfg->require_100rel = pjsua_var.ua_cfg.require_100rel;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000137}
138
139PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg)
140{
141 pj_bzero(cfg, sizeof(*cfg));
142}
143
144PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg)
145{
146 pj_bzero(cfg, sizeof(*cfg));
147
148 cfg->clock_rate = PJSUA_DEFAULT_CLOCK_RATE;
Benny Prijonocf0b4b22007-10-06 17:31:09 +0000149 cfg->audio_frame_ptime = 10;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000150 cfg->max_media_ports = 32;
151 cfg->has_ioqueue = PJ_TRUE;
152 cfg->thread_cnt = 1;
153 cfg->quality = PJSUA_DEFAULT_CODEC_QUALITY;
154 cfg->ilbc_mode = PJSUA_DEFAULT_ILBC_MODE;
155 cfg->ec_tail_len = PJSUA_DEFAULT_EC_TAIL_LEN;
156 cfg->jb_init = cfg->jb_min_pre = cfg->jb_max_pre = cfg->jb_max = -1;
157}
158
Benny Prijonodc39fe82006-05-26 12:17:46 +0000159
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000160/*****************************************************************************
161 * This is a very simple PJSIP module, whose sole purpose is to display
162 * incoming and outgoing messages to log. This module will have priority
163 * higher than transport layer, which means:
164 *
165 * - incoming messages will come to this module first before reaching
166 * transaction layer.
167 *
168 * - outgoing messages will come to this module last, after the message
169 * has been 'printed' to contiguous buffer by transport layer and
170 * appropriate transport instance has been decided for this message.
171 *
172 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000173
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000174/* Notification on incoming messages */
175static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
176{
Benny Prijonob4a17c92006-07-10 14:40:21 +0000177 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
178 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000179 "--end msg--",
180 rdata->msg_info.len,
181 pjsip_rx_data_get_info(rdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000182 rdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000183 rdata->pkt_info.src_name,
184 rdata->pkt_info.src_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000185 (int)rdata->msg_info.len,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000186 rdata->msg_info.msg_buf));
187
188 /* Always return false, otherwise messages will not get processed! */
189 return PJ_FALSE;
190}
Benny Prijonodc39fe82006-05-26 12:17:46 +0000191
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000192/* Notification on outgoing messages */
193static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
194{
195
196 /* Important note:
197 * tp_info field is only valid after outgoing messages has passed
198 * transport layer. So don't try to access tp_info when the module
199 * has lower priority than transport layer.
200 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000201
Benny Prijonob4a17c92006-07-10 14:40:21 +0000202 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
203 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000204 "--end msg--",
205 (tdata->buf.cur - tdata->buf.start),
206 pjsip_tx_data_get_info(tdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000207 tdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000208 tdata->tp_info.dst_name,
209 tdata->tp_info.dst_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000210 (int)(tdata->buf.cur - tdata->buf.start),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000211 tdata->buf.start));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000212
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000213 /* Always return success, otherwise message will not get sent! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000214 return PJ_SUCCESS;
215}
216
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000217/* The module instance. */
218static pjsip_module pjsua_msg_logger =
219{
220 NULL, NULL, /* prev, next. */
221 { "mod-pjsua-log", 13 }, /* Name. */
222 -1, /* Id */
223 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
224 NULL, /* load() */
225 NULL, /* start() */
226 NULL, /* stop() */
227 NULL, /* unload() */
228 &logging_on_rx_msg, /* on_rx_request() */
229 &logging_on_rx_msg, /* on_rx_response() */
230 &logging_on_tx_msg, /* on_tx_request. */
231 &logging_on_tx_msg, /* on_tx_response() */
232 NULL, /* on_tsx_state() */
233
234};
235
236
237/*****************************************************************************
Benny Prijono56315612006-07-18 14:39:40 +0000238 * Another simple module to handle incoming OPTIONS request
239 */
240
241/* Notification on incoming request */
242static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
243{
244 pjsip_tx_data *tdata;
245 pjsip_response_addr res_addr;
Benny Prijono617c5bc2007-04-02 19:51:21 +0000246 pjmedia_sock_info skinfo;
Benny Prijono56315612006-07-18 14:39:40 +0000247 pjmedia_sdp_session *sdp;
248 const pjsip_hdr *cap_hdr;
249 pj_status_t status;
250
251 /* Only want to handle OPTIONS requests */
252 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000253 pjsip_get_options_method()) != 0)
Benny Prijono56315612006-07-18 14:39:40 +0000254 {
255 return PJ_FALSE;
256 }
257
258 /* Create basic response. */
259 status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL,
260 &tdata);
261 if (status != PJ_SUCCESS) {
262 pjsua_perror(THIS_FILE, "Unable to create OPTIONS response", status);
263 return PJ_TRUE;
264 }
265
266 /* Add Allow header */
267 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ALLOW, NULL);
268 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000269 pjsip_msg_add_hdr(tdata->msg,
270 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000271 }
272
273 /* Add Accept header */
274 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL);
275 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000276 pjsip_msg_add_hdr(tdata->msg,
277 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000278 }
279
280 /* Add Supported header */
281 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_SUPPORTED, NULL);
282 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000283 pjsip_msg_add_hdr(tdata->msg,
284 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000285 }
286
287 /* Add Allow-Events header from the evsub module */
288 cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL);
289 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000290 pjsip_msg_add_hdr(tdata->msg,
291 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000292 }
293
294 /* Add User-Agent header */
295 if (pjsua_var.ua_cfg.user_agent.slen) {
296 const pj_str_t USER_AGENT = { "User-Agent", 10};
297 pjsip_hdr *h;
298
299 h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
300 &USER_AGENT,
301 &pjsua_var.ua_cfg.user_agent);
302 pjsip_msg_add_hdr(tdata->msg, h);
303 }
304
Benny Prijono617c5bc2007-04-02 19:51:21 +0000305 /* Get media socket info */
306 pjmedia_transport_get_info(pjsua_var.calls[0].med_tp, &skinfo);
307
Benny Prijono56315612006-07-18 14:39:40 +0000308 /* Add SDP body, using call0's RTP address */
309 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool, 1,
Benny Prijono617c5bc2007-04-02 19:51:21 +0000310 &skinfo, &sdp);
Benny Prijono56315612006-07-18 14:39:40 +0000311 if (status == PJ_SUCCESS) {
312 pjsip_create_sdp_body(tdata->pool, sdp, &tdata->msg->body);
313 }
314
315 /* Send response statelessly */
316 pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
317 status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, tdata, NULL, NULL);
318 if (status != PJ_SUCCESS)
319 pjsip_tx_data_dec_ref(tdata);
320
321 return PJ_TRUE;
322}
323
324
325/* The module instance. */
326static pjsip_module pjsua_options_handler =
327{
328 NULL, NULL, /* prev, next. */
329 { "mod-pjsua-options", 17 }, /* Name. */
330 -1, /* Id */
331 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
332 NULL, /* load() */
333 NULL, /* start() */
334 NULL, /* stop() */
335 NULL, /* unload() */
336 &options_on_rx_request, /* on_rx_request() */
337 NULL, /* on_rx_response() */
338 NULL, /* on_tx_request. */
339 NULL, /* on_tx_response() */
340 NULL, /* on_tsx_state() */
341
342};
343
344
345/*****************************************************************************
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000346 * These two functions are the main callbacks registered to PJSIP stack
347 * to receive SIP request and response messages that are outside any
348 * dialogs and any transactions.
349 */
Benny Prijono268ca612006-02-07 12:34:11 +0000350
351/*
352 * Handler for receiving incoming requests.
353 *
354 * This handler serves multiple purposes:
355 * - it receives requests outside dialogs.
356 * - it receives requests inside dialogs, when the requests are
357 * unhandled by other dialog usages. Example of these
358 * requests are: MESSAGE.
359 */
360static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
361{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000362 pj_bool_t processed = PJ_FALSE;
363
364 PJSUA_LOCK();
Benny Prijono38998232006-02-08 22:44:25 +0000365
Benny Prijono84126ab2006-02-09 09:30:09 +0000366 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
Benny Prijono38998232006-02-08 22:44:25 +0000367
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000368 processed = pjsua_call_on_incoming(rdata);
Benny Prijono38998232006-02-08 22:44:25 +0000369 }
370
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000371 PJSUA_UNLOCK();
372
373 return processed;
Benny Prijono268ca612006-02-07 12:34:11 +0000374}
375
376
377/*
378 * Handler for receiving incoming responses.
379 *
380 * This handler serves multiple purposes:
381 * - it receives strayed responses (i.e. outside any dialog and
382 * outside any transactions).
383 * - it receives responses coming to a transaction, when pjsua
384 * module is set as transaction user for the transaction.
385 * - it receives responses inside a dialog, when these responses
386 * are unhandled by other dialog usages.
387 */
388static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
389{
390 PJ_UNUSED_ARG(rdata);
Benny Prijono268ca612006-02-07 12:34:11 +0000391 return PJ_FALSE;
392}
393
394
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000395/*****************************************************************************
396 * Logging.
397 */
398
399/* Log callback */
400static void log_writer(int level, const char *buffer, int len)
Benny Prijono268ca612006-02-07 12:34:11 +0000401{
Benny Prijono572d4852006-11-23 21:50:02 +0000402 /* Write to file, stdout or application callback. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000403
404 if (pjsua_var.log_file) {
405 pj_ssize_t size = len;
406 pj_file_write(pjsua_var.log_file, buffer, &size);
Benny Prijono980ca0a2007-04-03 17:55:08 +0000407 /* This will slow things down considerably! Don't do it!
408 pj_file_flush(pjsua_var.log_file);
409 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000410 }
411
Benny Prijono572d4852006-11-23 21:50:02 +0000412 if (level <= (int)pjsua_var.log_cfg.console_level) {
413 if (pjsua_var.log_cfg.cb)
414 (*pjsua_var.log_cfg.cb)(level, buffer, len);
415 else
416 pj_log_write(level, buffer, len);
417 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000418}
419
420
421/*
422 * Application can call this function at any time (after pjsua_create(), of
423 * course) to change logging settings.
424 */
425PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg)
426{
427 pj_status_t status;
428
429 /* Save config. */
430 pjsua_logging_config_dup(pjsua_var.pool, &pjsua_var.log_cfg, cfg);
431
432 /* Redirect log function to ours */
433 pj_log_set_log_func( &log_writer );
434
Benny Prijono9cb09a22007-08-30 09:35:10 +0000435 /* Set decor */
436 pj_log_set_decor(pjsua_var.log_cfg.decor);
437
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000438 /* Close existing file, if any */
439 if (pjsua_var.log_file) {
440 pj_file_close(pjsua_var.log_file);
441 pjsua_var.log_file = NULL;
442 }
443
444 /* If output log file is desired, create the file: */
445 if (pjsua_var.log_cfg.log_filename.slen) {
446
447 status = pj_file_open(pjsua_var.pool,
448 pjsua_var.log_cfg.log_filename.ptr,
449 PJ_O_WRONLY,
450 &pjsua_var.log_file);
451
452 if (status != PJ_SUCCESS) {
453 pjsua_perror(THIS_FILE, "Error creating log file", status);
454 return status;
455 }
456 }
457
458 /* Unregister msg logging if it's previously registered */
459 if (pjsua_msg_logger.id >= 0) {
460 pjsip_endpt_unregister_module(pjsua_var.endpt, &pjsua_msg_logger);
461 pjsua_msg_logger.id = -1;
462 }
463
464 /* Enable SIP message logging */
465 if (pjsua_var.log_cfg.msg_logging)
466 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_msg_logger);
467
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000468 return PJ_SUCCESS;
469}
470
471
472/*****************************************************************************
473 * PJSUA Base API.
474 */
475
476/* Worker thread function. */
477static int worker_thread(void *arg)
478{
479 enum { TIMEOUT = 10 };
Benny Prijonof8baa872006-02-27 23:54:23 +0000480
Benny Prijono268ca612006-02-07 12:34:11 +0000481 PJ_UNUSED_ARG(arg);
482
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000483 while (!pjsua_var.thread_quit_flag) {
484 int count;
485
486 count = pjsua_handle_events(TIMEOUT);
487 if (count < 0)
488 pj_thread_sleep(TIMEOUT);
489 }
Benny Prijono268ca612006-02-07 12:34:11 +0000490
491 return 0;
492}
493
Benny Prijonodc39fe82006-05-26 12:17:46 +0000494
Benny Prijono268ca612006-02-07 12:34:11 +0000495/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000496 * Instantiate pjsua application.
Benny Prijonodc39fe82006-05-26 12:17:46 +0000497 */
498PJ_DEF(pj_status_t) pjsua_create(void)
499{
500 pj_status_t status;
Benny Prijono268ca612006-02-07 12:34:11 +0000501
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000502 /* Init pjsua data */
503 init_data();
Benny Prijono268ca612006-02-07 12:34:11 +0000504
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000505 /* Set default logging settings */
506 pjsua_logging_config_default(&pjsua_var.log_cfg);
507
508 /* Init PJLIB: */
Benny Prijono268ca612006-02-07 12:34:11 +0000509 status = pj_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000510 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
511
Benny Prijono268ca612006-02-07 12:34:11 +0000512
Benny Prijonofccab712006-02-22 22:23:22 +0000513 /* Init PJLIB-UTIL: */
Benny Prijonofccab712006-02-22 22:23:22 +0000514 status = pjlib_util_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000515 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonofccab712006-02-22 22:23:22 +0000516
Benny Prijonoec921342007-03-24 13:00:30 +0000517 /* Init PJNATH */
518 status = pjnath_init();
519 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono268ca612006-02-07 12:34:11 +0000520
Benny Prijono094d3ad2006-11-21 08:41:00 +0000521 /* Set default sound device ID */
522 pjsua_var.cap_dev = pjsua_var.play_dev = -1;
523
Benny Prijono268ca612006-02-07 12:34:11 +0000524 /* Init caching pool. */
Benny Prijono106f5b72007-08-30 13:49:33 +0000525 pj_caching_pool_init(&pjsua_var.cp, NULL, 0);
Benny Prijono268ca612006-02-07 12:34:11 +0000526
527 /* Create memory pool for application. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000528 pjsua_var.pool = pjsua_pool_create("pjsua", 4000, 4000);
529
530 PJ_ASSERT_RETURN(pjsua_var.pool, PJ_ENOMEM);
Benny Prijono268ca612006-02-07 12:34:11 +0000531
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000532 /* Create mutex */
533 status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua",
534 &pjsua_var.mutex);
Benny Prijonoccf95622006-02-07 18:48:01 +0000535 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000536 pjsua_perror(THIS_FILE, "Unable to create mutex", status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000537 return status;
538 }
539
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000540 /* Must create SIP endpoint to initialize SIP parser. The parser
541 * is needed for example when application needs to call pjsua_verify_url().
Benny Prijonob8c25182006-04-29 08:31:09 +0000542 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000543 status = pjsip_endpt_create(&pjsua_var.cp.factory,
544 pj_gethostname()->ptr,
545 &pjsua_var.endpt);
546 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono39879152006-02-23 02:09:10 +0000547
548
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000549 return PJ_SUCCESS;
550}
551
552
553/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000554 * Initialize pjsua with the specified settings. All the settings are
555 * optional, and the default values will be used when the config is not
556 * specified.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000557 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000558PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
559 const pjsua_logging_config *log_cfg,
560 const pjsua_media_config *media_cfg)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000561{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000562 pjsua_config default_cfg;
563 pjsua_media_config default_media_cfg;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000564 const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
Benny Prijonodc39fe82006-05-26 12:17:46 +0000565 pj_status_t status;
566
567
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000568 /* Create default configurations when the config is not supplied */
569
570 if (ua_cfg == NULL) {
571 pjsua_config_default(&default_cfg);
572 ua_cfg = &default_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000573 }
574
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000575 if (media_cfg == NULL) {
576 pjsua_media_config_default(&default_media_cfg);
577 media_cfg = &default_media_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000578 }
579
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000580 /* Initialize logging first so that info/errors can be captured */
581 if (log_cfg) {
582 status = pjsua_reconfigure_logging(log_cfg);
583 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000584 }
585
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000586 /* If nameserver is configured, create DNS resolver instance and
587 * set it to be used by SIP resolver.
588 */
589 if (ua_cfg->nameserver_count) {
590#if PJSIP_HAS_RESOLVER
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000591 unsigned i;
592
593 /* Create DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000594 status = pjsip_endpt_create_resolver(pjsua_var.endpt,
595 &pjsua_var.resolver);
Benny Prijono318f3842007-05-15 20:27:08 +0000596 if (status != PJ_SUCCESS) {
597 pjsua_perror(THIS_FILE, "Error creating resolver", status);
598 return status;
599 }
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000600
601 /* Configure nameserver for the DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000602 status = pj_dns_resolver_set_ns(pjsua_var.resolver,
603 ua_cfg->nameserver_count,
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000604 ua_cfg->nameserver, NULL);
605 if (status != PJ_SUCCESS) {
606 pjsua_perror(THIS_FILE, "Error setting nameserver", status);
607 return status;
608 }
609
610 /* Set this DNS resolver to be used by the SIP resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000611 status = pjsip_endpt_set_resolver(pjsua_var.endpt, pjsua_var.resolver);
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000612 if (status != PJ_SUCCESS) {
613 pjsua_perror(THIS_FILE, "Error setting DNS resolver", status);
614 return status;
615 }
616
617 /* Print nameservers */
618 for (i=0; i<ua_cfg->nameserver_count; ++i) {
619 PJ_LOG(4,(THIS_FILE, "Nameserver %.*s added",
620 (int)ua_cfg->nameserver[i].slen,
621 ua_cfg->nameserver[i].ptr));
622 }
623#else
624 PJ_LOG(2,(THIS_FILE,
625 "DNS resolver is disabled (PJSIP_HAS_RESOLVER==0)"));
626#endif
627 }
628
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000629 /* Init SIP UA: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000630
631 /* Initialize transaction layer: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000632 status = pjsip_tsx_layer_init_module(pjsua_var.endpt);
633 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000634
Benny Prijonodc39fe82006-05-26 12:17:46 +0000635
636 /* Initialize UA layer module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000637 status = pjsip_ua_init_module( pjsua_var.endpt, NULL );
638 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000639
Benny Prijonodc39fe82006-05-26 12:17:46 +0000640
Benny Prijono053f5222006-11-11 16:16:04 +0000641 /* Initialize Replaces support. */
642 status = pjsip_replaces_init_module( pjsua_var.endpt );
643 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
644
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000645 /* Initialize 100rel support */
646 status = pjsip_100rel_init_module(pjsua_var.endpt);
647 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono053f5222006-11-11 16:16:04 +0000648
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000649 /* Initialize and register PJSUA application module. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000650 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000651 const pjsip_module mod_initializer =
Benny Prijonodc39fe82006-05-26 12:17:46 +0000652 {
653 NULL, NULL, /* prev, next. */
654 { "mod-pjsua", 9 }, /* Name. */
655 -1, /* Id */
656 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
657 NULL, /* load() */
658 NULL, /* start() */
659 NULL, /* stop() */
660 NULL, /* unload() */
661 &mod_pjsua_on_rx_request, /* on_rx_request() */
662 &mod_pjsua_on_rx_response, /* on_rx_response() */
663 NULL, /* on_tx_request. */
664 NULL, /* on_tx_response() */
665 NULL, /* on_tsx_state() */
666 };
667
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000668 pjsua_var.mod = mod_initializer;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000669
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000670 status = pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_var.mod);
671 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000672 }
673
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000674
Benny Prijonodc39fe82006-05-26 12:17:46 +0000675
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000676 /* Initialize PJSUA call subsystem: */
677 status = pjsua_call_subsys_init(ua_cfg);
678 if (status != PJ_SUCCESS)
Benny Prijonodc39fe82006-05-26 12:17:46 +0000679 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000680
681
Benny Prijonoc97608e2007-03-23 16:34:20 +0000682 /* Start resolving STUN server */
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000683
Benny Prijonoc97608e2007-03-23 16:34:20 +0000684 status = pjsua_resolve_stun_server(PJ_FALSE);
685 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
686 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
687 return status;
688 }
689
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000690 /* Initialize PJSUA media subsystem */
691 status = pjsua_media_subsys_init(media_cfg);
692 if (status != PJ_SUCCESS)
693 goto on_error;
694
Benny Prijonodc39fe82006-05-26 12:17:46 +0000695
696 /* Init core SIMPLE module : */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000697 status = pjsip_evsub_init_module(pjsua_var.endpt);
698 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000699
Benny Prijonodc39fe82006-05-26 12:17:46 +0000700
701 /* Init presence module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000702 status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance());
703 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000704
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000705 /* Init PUBLISH module */
706 pjsip_publishc_init_module(pjsua_var.endpt);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000707
708 /* Init xfer/REFER module */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000709 status = pjsip_xfer_init_module( pjsua_var.endpt );
710 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000711
712 /* Init pjsua presence handler: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000713 status = pjsua_pres_init();
714 if (status != PJ_SUCCESS)
715 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000716
717 /* Init out-of-dialog MESSAGE request handler. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000718 status = pjsua_im_init();
719 if (status != PJ_SUCCESS)
720 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000721
Benny Prijonoc8141a82006-08-20 09:12:19 +0000722 /* Register OPTIONS handler */
723 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_options_handler);
724
725 /* Add OPTIONS in Allow header */
726 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_ALLOW,
727 NULL, 1, &STR_OPTIONS);
728
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000729 /* Start worker thread if needed. */
730 if (pjsua_var.ua_cfg.thread_cnt) {
731 unsigned i;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000732
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000733 if (pjsua_var.ua_cfg.thread_cnt > PJ_ARRAY_SIZE(pjsua_var.thread))
734 pjsua_var.ua_cfg.thread_cnt = PJ_ARRAY_SIZE(pjsua_var.thread);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000735
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000736 for (i=0; i<pjsua_var.ua_cfg.thread_cnt; ++i) {
737 status = pj_thread_create(pjsua_var.pool, "pjsua", &worker_thread,
738 NULL, 0, 0, &pjsua_var.thread[i]);
739 if (status != PJ_SUCCESS)
740 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000741 }
Benny Prijonof521eb02006-08-06 23:07:25 +0000742 PJ_LOG(4,(THIS_FILE, "%d SIP worker threads created",
743 pjsua_var.ua_cfg.thread_cnt));
744 } else {
745 PJ_LOG(4,(THIS_FILE, "No SIP worker threads created"));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000746 }
747
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000748 /* Done! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000749
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000750 PJ_LOG(3,(THIS_FILE, "pjsua version %s for %s initialized",
Benny Prijono106f5b72007-08-30 13:49:33 +0000751 pj_get_version(), PJ_OS_NAME));
Benny Prijonoccf95622006-02-07 18:48:01 +0000752
Benny Prijono268ca612006-02-07 12:34:11 +0000753 return PJ_SUCCESS;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000754
755on_error:
756 pjsua_destroy();
757 return status;
Benny Prijono268ca612006-02-07 12:34:11 +0000758}
759
760
Benny Prijono834aee32006-02-19 01:38:06 +0000761/* Sleep with polling */
762static void busy_sleep(unsigned msec)
763{
Benny Prijonob2c96822007-05-03 13:31:21 +0000764#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
Benny Prijono897f9f82007-05-03 19:56:21 +0000765 /* Ideally we shouldn't call pj_thread_sleep() and rather
766 * CActiveScheduler::WaitForAnyRequest() here, but that will
767 * drag in Symbian header and it doesn't look pretty.
768 */
Benny Prijonob2c96822007-05-03 13:31:21 +0000769 pj_thread_sleep(msec);
770#else
Benny Prijono834aee32006-02-19 01:38:06 +0000771 pj_time_val timeout, now;
772
773 pj_gettimeofday(&timeout);
774 timeout.msec += msec;
775 pj_time_val_normalize(&timeout);
776
777 do {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000778 while (pjsua_handle_events(10) > 0)
779 ;
Benny Prijono834aee32006-02-19 01:38:06 +0000780 pj_gettimeofday(&now);
781 } while (PJ_TIME_VAL_LT(now, timeout));
Benny Prijonob2c96822007-05-03 13:31:21 +0000782#endif
Benny Prijono834aee32006-02-19 01:38:06 +0000783}
784
Benny Prijonoebbf6892007-03-24 17:37:25 +0000785
Benny Prijonoebbf6892007-03-24 17:37:25 +0000786/*
787 * Callback function to receive notification from the resolver
788 * when the resolution process completes.
789 */
790static void stun_dns_srv_resolver_cb(void *user_data,
791 pj_status_t status,
792 const pj_dns_srv_record *rec)
793{
794 unsigned i;
795
796 PJ_UNUSED_ARG(user_data);
797
798 pjsua_var.stun_status = status;
799
800 if (status != PJ_SUCCESS) {
801 /* DNS SRV resolution failed. If stun_host is specified, resolve
802 * it with gethostbyname()
803 */
804 if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000805 pj_str_t str_host, str_port;
806 int port;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000807 pj_hostent he;
808
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000809 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
810 if (str_port.ptr != NULL) {
811 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
812 str_host.slen = (str_port.ptr - str_host.ptr);
813 str_port.ptr++;
814 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
815 str_host.slen - 1;
816 port = (int)pj_strtoul(&str_port);
817 if (port < 1 || port > 65535) {
818 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
819 pjsua_var.stun_status = PJ_EINVAL;
820 return;
821 }
822 } else {
823 str_host = pjsua_var.ua_cfg.stun_host;
824 port = 3478;
825 }
826
827 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000828
829 if (pjsua_var.stun_status == PJ_SUCCESS) {
830 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
831 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000832 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000833
834 PJ_LOG(3,(THIS_FILE,
835 "STUN server %.*s resolved, address is %s:%d",
836 (int)pjsua_var.ua_cfg.stun_host.slen,
837 pjsua_var.ua_cfg.stun_host.ptr,
838 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
839 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
840 }
841 } else {
842 char errmsg[PJ_ERR_MSG_SIZE];
843
844 pj_strerror(status, errmsg, sizeof(errmsg));
845 PJ_LOG(1,(THIS_FILE,
846 "DNS SRV resolution failed for STUN server %.*s: %s",
847 (int)pjsua_var.ua_cfg.stun_domain.slen,
848 pjsua_var.ua_cfg.stun_domain.ptr,
849 errmsg));
850 }
851 return;
852 }
853
Benny Prijonof5afa922007-06-11 16:51:18 +0000854 pj_assert(rec->count != 0 && rec->entry[0].server.addr_count != 0);
855 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL,
856 rec->entry[0].port);
857 pjsua_var.stun_srv.ipv4.sin_addr.s_addr =
858 rec->entry[0].server.addr[0].s_addr;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000859
860 PJ_LOG(3,(THIS_FILE, "_stun._udp.%.*s resolved, found %d entry(s):",
861 (int)pjsua_var.ua_cfg.stun_domain.slen,
862 pjsua_var.ua_cfg.stun_domain.ptr,
863 rec->count));
864
865 for (i=0; i<rec->count; ++i) {
866 PJ_LOG(3,(THIS_FILE,
867 " %d: prio=%d, weight=%d %s:%d",
868 i, rec->entry[i].priority, rec->entry[i].weight,
Benny Prijonof5afa922007-06-11 16:51:18 +0000869 pj_inet_ntoa(rec->entry[i].server.addr[0]),
870 (int)rec->entry[i].port));
Benny Prijonoebbf6892007-03-24 17:37:25 +0000871 }
872
873}
874
Benny Prijono268ca612006-02-07 12:34:11 +0000875/*
Benny Prijonoc97608e2007-03-23 16:34:20 +0000876 * Resolve STUN server.
877 */
878pj_status_t pjsua_resolve_stun_server(pj_bool_t wait)
879{
880 if (pjsua_var.stun_status == PJ_EUNKNOWN) {
881 /* Initialize STUN configuration */
882 pj_stun_config_init(&pjsua_var.stun_cfg, &pjsua_var.cp.factory, 0,
883 pjsip_endpt_get_ioqueue(pjsua_var.endpt),
884 pjsip_endpt_get_timer_heap(pjsua_var.endpt));
885
886 /* Start STUN server resolution */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000887
888 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000889
Benny Prijonoebbf6892007-03-24 17:37:25 +0000890 /* If stun_domain is specified, resolve STUN servers with DNS
891 * SRV resolution.
892 */
893 if (pjsua_var.ua_cfg.stun_domain.slen) {
894 pj_str_t res_type;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000895 pj_status_t status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000896
897 /* Fail if resolver is not configured */
898 if (pjsua_var.resolver == NULL) {
899 PJ_LOG(1,(THIS_FILE, "Nameserver must be configured when "
900 "stun_domain is specified"));
901 pjsua_var.stun_status = PJLIB_UTIL_EDNSNONS;
902 return PJLIB_UTIL_EDNSNONS;
903 }
904 res_type = pj_str("_stun._udp");
Benny Prijonoda9785b2007-04-02 20:43:06 +0000905 status =
Benny Prijonoebbf6892007-03-24 17:37:25 +0000906 pj_dns_srv_resolve(&pjsua_var.ua_cfg.stun_domain, &res_type,
907 3478, pjsua_var.pool, pjsua_var.resolver,
Benny Prijonof5afa922007-06-11 16:51:18 +0000908 0, NULL, &stun_dns_srv_resolver_cb, NULL);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000909 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +0000910 pjsua_perror(THIS_FILE, "Error starting DNS SRV resolution",
911 pjsua_var.stun_status);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000912 pjsua_var.stun_status = status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000913 return pjsua_var.stun_status;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000914 } else {
915 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000916 }
917 }
918 /* Otherwise if stun_host is specified, resolve STUN server with
919 * gethostbyname().
920 */
921 else if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000922 pj_str_t str_host, str_port;
923 int port;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000924 pj_hostent he;
925
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000926 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
927 if (str_port.ptr != NULL) {
928 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
929 str_host.slen = (str_port.ptr - str_host.ptr);
930 str_port.ptr++;
931 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
932 str_host.slen - 1;
933 port = (int)pj_strtoul(&str_port);
934 if (port < 1 || port > 65535) {
935 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
936 pjsua_var.stun_status = PJ_EINVAL;
937 return pjsua_var.stun_status;
938 }
939 } else {
940 str_host = pjsua_var.ua_cfg.stun_host;
941 port = 3478;
942 }
943
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000944 pjsua_var.stun_status =
945 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, &str_host,
946 (pj_uint16_t)port);
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000947
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000948 if (pjsua_var.stun_status != PJ_SUCCESS) {
949 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000950
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000951 if (pjsua_var.stun_status == PJ_SUCCESS) {
952 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
953 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
954 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
955 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000956 }
Benny Prijonoebbf6892007-03-24 17:37:25 +0000957
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000958 PJ_LOG(3,(THIS_FILE,
959 "STUN server %.*s resolved, address is %s:%d",
960 (int)pjsua_var.ua_cfg.stun_host.slen,
961 pjsua_var.ua_cfg.stun_host.ptr,
962 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
963 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
964
Benny Prijonoc97608e2007-03-23 16:34:20 +0000965 }
Benny Prijonoebbf6892007-03-24 17:37:25 +0000966 /* Otherwise disable STUN. */
967 else {
968 pjsua_var.stun_status = PJ_SUCCESS;
969 }
970
971
Benny Prijonoc97608e2007-03-23 16:34:20 +0000972 return pjsua_var.stun_status;
973
974 } else if (pjsua_var.stun_status == PJ_EPENDING) {
975 /* STUN server resolution has been started, wait for the
976 * result.
977 */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000978 if (wait) {
979 while (pjsua_var.stun_status == PJ_EPENDING)
980 pjsua_handle_events(10);
981 }
982
983 return pjsua_var.stun_status;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000984
985 } else {
986 /* STUN server has been resolved, return the status */
987 return pjsua_var.stun_status;
988 }
989}
990
991/*
Benny Prijono268ca612006-02-07 12:34:11 +0000992 * Destroy pjsua.
993 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000994PJ_DEF(pj_status_t) pjsua_destroy(void)
Benny Prijono268ca612006-02-07 12:34:11 +0000995{
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000996 int i; /* Must be signed */
Benny Prijono268ca612006-02-07 12:34:11 +0000997
998 /* Signal threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000999 pjsua_var.thread_quit_flag = 1;
Benny Prijono268ca612006-02-07 12:34:11 +00001000
1001 /* Wait worker threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001002 for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) {
1003 if (pjsua_var.thread[i]) {
1004 pj_thread_join(pjsua_var.thread[i]);
1005 pj_thread_destroy(pjsua_var.thread[i]);
1006 pjsua_var.thread[i] = NULL;
Benny Prijonoe4f2abb2006-02-10 14:04:05 +00001007 }
Benny Prijono268ca612006-02-07 12:34:11 +00001008 }
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001009
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001010 if (pjsua_var.endpt) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001011 /* Terminate all calls. */
1012 pjsua_call_hangup_all();
1013
1014 /* Terminate all presence subscriptions. */
1015 pjsua_pres_shutdown();
1016
1017 /* Unregister, if required: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001018 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1019 if (!pjsua_var.acc[i].valid)
1020 continue;
1021
1022 if (pjsua_var.acc[i].regc) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001023 pjsua_acc_set_registration(i, PJ_FALSE);
1024 }
1025 }
1026
1027 /* Wait for some time to allow unregistration to complete: */
Benny Prijono9fc735d2006-05-28 14:58:12 +00001028 PJ_LOG(4,(THIS_FILE, "Shutting down..."));
1029 busy_sleep(1000);
1030 }
Benny Prijono834aee32006-02-19 01:38:06 +00001031
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001032 /* Destroy media */
1033 pjsua_media_subsys_destroy();
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001034
Benny Prijono268ca612006-02-07 12:34:11 +00001035 /* Destroy endpoint. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001036 if (pjsua_var.endpt) {
1037 pjsip_endpt_destroy(pjsua_var.endpt);
1038 pjsua_var.endpt = NULL;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001039 }
Benny Prijono268ca612006-02-07 12:34:11 +00001040
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001041 /* Destroy mutex */
1042 if (pjsua_var.mutex) {
1043 pj_mutex_destroy(pjsua_var.mutex);
1044 pjsua_var.mutex = NULL;
1045 }
1046
1047 /* Destroy pool and pool factory. */
1048 if (pjsua_var.pool) {
1049 pj_pool_release(pjsua_var.pool);
1050 pjsua_var.pool = NULL;
1051 pj_caching_pool_destroy(&pjsua_var.cp);
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00001052
1053 PJ_LOG(4,(THIS_FILE, "PJSUA destroyed..."));
1054
1055 /* End logging */
1056 if (pjsua_var.log_file) {
1057 pj_file_close(pjsua_var.log_file);
1058 pjsua_var.log_file = NULL;
1059 }
1060
1061 /* Shutdown PJLIB */
1062 pj_shutdown();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001063 }
Benny Prijono268ca612006-02-07 12:34:11 +00001064
Benny Prijonof762ee72006-12-01 11:14:37 +00001065 /* Clear pjsua_var */
1066 pj_bzero(&pjsua_var, sizeof(pjsua_var));
1067
Benny Prijono268ca612006-02-07 12:34:11 +00001068 /* Done. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001069 return PJ_SUCCESS;
1070}
1071
1072
1073/**
1074 * Application is recommended to call this function after all initialization
1075 * is done, so that the library can do additional checking set up
1076 * additional
1077 *
1078 * @return PJ_SUCCESS on success, or the appropriate error code.
1079 */
1080PJ_DEF(pj_status_t) pjsua_start(void)
1081{
1082 pj_status_t status;
1083
1084 status = pjsua_call_subsys_start();
1085 if (status != PJ_SUCCESS)
1086 return status;
1087
1088 status = pjsua_media_subsys_start();
1089 if (status != PJ_SUCCESS)
1090 return status;
1091
1092 status = pjsua_pres_start();
1093 if (status != PJ_SUCCESS)
1094 return status;
Benny Prijono268ca612006-02-07 12:34:11 +00001095
1096 return PJ_SUCCESS;
1097}
1098
Benny Prijono9fc735d2006-05-28 14:58:12 +00001099
1100/**
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001101 * Poll pjsua for events, and if necessary block the caller thread for
1102 * the specified maximum interval (in miliseconds).
1103 */
1104PJ_DEF(int) pjsua_handle_events(unsigned msec_timeout)
1105{
Benny Prijono897f9f82007-05-03 19:56:21 +00001106#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
1107 /* Ideally we shouldn't call pj_thread_sleep() and rather
1108 * CActiveScheduler::WaitForAnyRequest() here, but that will
1109 * drag in Symbian header and it doesn't look pretty.
1110 */
1111 pj_thread_sleep(msec_timeout);
1112 return msec_timeout;
1113#else
1114
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001115 unsigned count = 0;
1116 pj_time_val tv;
1117 pj_status_t status;
1118
1119 tv.sec = 0;
1120 tv.msec = msec_timeout;
1121 pj_time_val_normalize(&tv);
1122
1123 status = pjsip_endpt_handle_events2(pjsua_var.endpt, &tv, &count);
1124
1125 if (status != PJ_SUCCESS)
1126 return -status;
1127
1128 return count;
Benny Prijono897f9f82007-05-03 19:56:21 +00001129#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001130}
1131
1132
1133/*
1134 * Create memory pool.
1135 */
1136PJ_DEF(pj_pool_t*) pjsua_pool_create( const char *name, pj_size_t init_size,
1137 pj_size_t increment)
1138{
1139 /* Pool factory is thread safe, no need to lock */
1140 return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment,
1141 NULL);
1142}
1143
1144
1145/*
1146 * Internal function to get SIP endpoint instance of pjsua, which is
1147 * needed for example to register module, create transports, etc.
1148 * Probably is only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001149 */
1150PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void)
1151{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001152 return pjsua_var.endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001153}
1154
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001155/*
1156 * Internal function to get media endpoint instance.
1157 * Only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001158 */
1159PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void)
1160{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001161 return pjsua_var.med_endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001162}
1163
Benny Prijono97b87172006-08-24 14:25:14 +00001164/*
1165 * Internal function to get PJSUA pool factory.
1166 */
1167PJ_DEF(pj_pool_factory*) pjsua_get_pool_factory(void)
1168{
1169 return &pjsua_var.cp.factory;
1170}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001171
1172/*****************************************************************************
1173 * PJSUA SIP Transport API.
1174 */
1175
1176/*
1177 * Create and initialize SIP socket (and possibly resolve public
1178 * address via STUN, depending on config).
1179 */
1180static pj_status_t create_sip_udp_sock(pj_in_addr bound_addr,
1181 int port,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001182 pj_sock_t *p_sock,
1183 pj_sockaddr_in *p_pub_addr)
1184{
Benny Prijonoc97608e2007-03-23 16:34:20 +00001185 char ip_addr[32];
1186 pj_str_t stun_srv;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001187 pj_sock_t sock;
1188 pj_status_t status;
1189
Benny Prijonoc97608e2007-03-23 16:34:20 +00001190 /* Make sure STUN server resolution has completed */
1191 status = pjsua_resolve_stun_server(PJ_TRUE);
1192 if (status != PJ_SUCCESS) {
1193 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
1194 return status;
1195 }
1196
Benny Prijono8ab968f2007-07-20 08:08:30 +00001197 status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001198 if (status != PJ_SUCCESS) {
1199 pjsua_perror(THIS_FILE, "socket() error", status);
Benny Prijonod8410532006-06-15 11:04:33 +00001200 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001201 }
1202
Benny Prijonof90ef4f2007-02-12 14:59:57 +00001203 status = pj_sock_bind_in(sock, pj_ntohl(bound_addr.s_addr),
1204 (pj_uint16_t)port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001205 if (status != PJ_SUCCESS) {
1206 pjsua_perror(THIS_FILE, "bind() error", status);
1207 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001208 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001209 }
1210
Benny Prijonoe347cb02007-02-14 14:36:13 +00001211 /* If port is zero, get the bound port */
1212 if (port == 0) {
1213 pj_sockaddr_in bound_addr;
1214 int namelen = sizeof(bound_addr);
1215 status = pj_sock_getsockname(sock, &bound_addr, &namelen);
1216 if (status != PJ_SUCCESS) {
1217 pjsua_perror(THIS_FILE, "getsockname() error", status);
1218 pj_sock_close(sock);
1219 return status;
1220 }
1221
1222 port = pj_ntohs(bound_addr.sin_port);
1223 }
1224
Benny Prijonoc97608e2007-03-23 16:34:20 +00001225 if (pjsua_var.stun_srv.addr.sa_family != 0) {
1226 pj_ansi_strcpy(ip_addr,pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
1227 stun_srv = pj_str(ip_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001228 } else {
Benny Prijonoc97608e2007-03-23 16:34:20 +00001229 stun_srv.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001230 }
1231
1232 /* Get the published address, either by STUN or by resolving
1233 * the name of local host.
1234 */
Benny Prijonoc97608e2007-03-23 16:34:20 +00001235 if (stun_srv.slen) {
Benny Prijono0a5cad82006-09-26 13:21:02 +00001236 /*
1237 * STUN is specified, resolve the address with STUN.
1238 */
Benny Prijono14c2b862007-02-21 00:40:05 +00001239 status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001240 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
1241 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
Benny Prijonoc97608e2007-03-23 16:34:20 +00001242 p_pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001243 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +00001244 pjsua_perror(THIS_FILE, "Error contacting STUN server", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001245 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001246 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001247 }
1248
Benny Prijono0a5cad82006-09-26 13:21:02 +00001249 } else if (p_pub_addr->sin_addr.s_addr != 0) {
1250 /*
1251 * Public address is already specified, no need to resolve the
1252 * address, only set the port.
1253 */
Benny Prijonoe347cb02007-02-14 14:36:13 +00001254 if (p_pub_addr->sin_port == 0)
1255 p_pub_addr->sin_port = pj_htons((pj_uint16_t)port);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001256
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001257 } else {
1258
Benny Prijono594e4c52006-09-14 18:51:01 +00001259 pj_bzero(p_pub_addr, sizeof(pj_sockaddr_in));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001260
Benny Prijono594e4c52006-09-14 18:51:01 +00001261 status = pj_gethostip(&p_pub_addr->sin_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001262 if (status != PJ_SUCCESS) {
Benny Prijonob43bad72007-01-20 05:11:08 +00001263 pjsua_perror(THIS_FILE, "Unable to get local host IP", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001264 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001265 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001266 }
1267
Benny Prijono8ab968f2007-07-20 08:08:30 +00001268 p_pub_addr->sin_family = pj_AF_INET();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001269 p_pub_addr->sin_port = pj_htons((pj_uint16_t)port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001270 }
1271
1272 *p_sock = sock;
1273
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001274 PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
1275 pj_inet_ntoa(p_pub_addr->sin_addr),
1276 (int)pj_ntohs(p_pub_addr->sin_port)));
1277
Benny Prijonod8410532006-06-15 11:04:33 +00001278 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001279}
1280
1281
1282/*
1283 * Create SIP transport.
1284 */
1285PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
1286 const pjsua_transport_config *cfg,
1287 pjsua_transport_id *p_id)
1288{
1289 pjsip_transport *tp;
1290 unsigned id;
1291 pj_status_t status;
1292
1293 PJSUA_LOCK();
1294
1295 /* Find empty transport slot */
1296 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001297 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001298 break;
1299 }
1300
1301 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1302 status = PJ_ETOOMANY;
1303 pjsua_perror(THIS_FILE, "Error creating transport", status);
1304 goto on_return;
1305 }
1306
1307 /* Create the transport */
1308 if (type == PJSIP_TRANSPORT_UDP) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001309 /*
1310 * Create UDP transport.
1311 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001312 pjsua_transport_config config;
Benny Prijonod8410532006-06-15 11:04:33 +00001313 pj_sock_t sock = PJ_INVALID_SOCKET;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001314 pj_sockaddr_in bound_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001315 pj_sockaddr_in pub_addr;
1316 pjsip_host_port addr_name;
1317
1318 /* Supply default config if it's not specified */
1319 if (cfg == NULL) {
1320 pjsua_transport_config_default(&config);
1321 cfg = &config;
1322 }
1323
Benny Prijono0a5cad82006-09-26 13:21:02 +00001324 /* Initialize bound address, if any */
1325 bound_addr.sin_addr.s_addr = PJ_INADDR_ANY;
1326 if (cfg->bound_addr.slen) {
1327 status = pj_sockaddr_in_set_str_addr(&bound_addr,&cfg->bound_addr);
1328 if (status != PJ_SUCCESS) {
1329 pjsua_perror(THIS_FILE,
1330 "Unable to resolve transport bound address",
1331 status);
1332 goto on_return;
1333 }
1334 }
1335
1336 /* Initialize the public address from the config, if any */
1337 pj_sockaddr_in_init(&pub_addr, NULL, (pj_uint16_t)cfg->port);
1338 if (cfg->public_addr.slen) {
1339 status = pj_sockaddr_in_set_str_addr(&pub_addr, &cfg->public_addr);
1340 if (status != PJ_SUCCESS) {
1341 pjsua_perror(THIS_FILE,
1342 "Unable to resolve transport public address",
1343 status);
1344 goto on_return;
1345 }
1346 }
1347
1348 /* Create the socket and possibly resolve the address with STUN
1349 * (only when public address is not specified).
1350 */
1351 status = create_sip_udp_sock(bound_addr.sin_addr, cfg->port,
Benny Prijono0a5cad82006-09-26 13:21:02 +00001352 &sock, &pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001353 if (status != PJ_SUCCESS)
1354 goto on_return;
1355
1356 addr_name.host = pj_str(pj_inet_ntoa(pub_addr.sin_addr));
1357 addr_name.port = pj_ntohs(pub_addr.sin_port);
1358
1359 /* Create UDP transport */
1360 status = pjsip_udp_transport_attach( pjsua_var.endpt, sock,
1361 &addr_name, 1,
1362 &tp);
1363 if (status != PJ_SUCCESS) {
1364 pjsua_perror(THIS_FILE, "Error creating SIP UDP transport",
1365 status);
1366 pj_sock_close(sock);
1367 goto on_return;
1368 }
1369
Benny Prijonoe93e2872006-06-28 16:46:49 +00001370
1371 /* Save the transport */
1372 pjsua_var.tpdata[id].type = type;
1373 pjsua_var.tpdata[id].local_name = tp->local_name;
1374 pjsua_var.tpdata[id].data.tp = tp;
1375
Benny Prijono3569c0d2007-04-06 10:29:20 +00001376#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
1377
Benny Prijonoe93e2872006-06-28 16:46:49 +00001378 } else if (type == PJSIP_TRANSPORT_TCP) {
1379 /*
1380 * Create TCP transport.
1381 */
1382 pjsua_transport_config config;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001383 pjsip_host_port a_name;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001384 pjsip_tpfactory *tcp;
1385 pj_sockaddr_in local_addr;
1386
1387 /* Supply default config if it's not specified */
1388 if (cfg == NULL) {
1389 pjsua_transport_config_default(&config);
1390 cfg = &config;
1391 }
1392
1393 /* Init local address */
1394 pj_sockaddr_in_init(&local_addr, 0, 0);
1395
1396 if (cfg->port)
1397 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1398
Benny Prijono0a5cad82006-09-26 13:21:02 +00001399 if (cfg->bound_addr.slen) {
1400 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1401 if (status != PJ_SUCCESS) {
1402 pjsua_perror(THIS_FILE,
1403 "Unable to resolve transport bound address",
1404 status);
1405 goto on_return;
1406 }
1407 }
1408
1409 /* Init published name */
1410 pj_bzero(&a_name, sizeof(pjsip_host_port));
1411 if (cfg->public_addr.slen)
1412 a_name.host = cfg->public_addr;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001413
1414 /* Create the TCP transport */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001415 status = pjsip_tcp_transport_start2(pjsua_var.endpt, &local_addr,
1416 &a_name, 1, &tcp);
Benny Prijonoe93e2872006-06-28 16:46:49 +00001417
1418 if (status != PJ_SUCCESS) {
1419 pjsua_perror(THIS_FILE, "Error creating SIP TCP listener",
1420 status);
1421 goto on_return;
1422 }
1423
1424 /* Save the transport */
1425 pjsua_var.tpdata[id].type = type;
1426 pjsua_var.tpdata[id].local_name = tcp->addr_name;
1427 pjsua_var.tpdata[id].data.factory = tcp;
1428
Benny Prijono3569c0d2007-04-06 10:29:20 +00001429#endif /* PJ_HAS_TCP */
1430
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001431#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
1432 } else if (type == PJSIP_TRANSPORT_TLS) {
1433 /*
1434 * Create TLS transport.
1435 */
Benny Prijonof3bbc132006-12-25 06:43:59 +00001436 /*
1437 * Create TCP transport.
1438 */
1439 pjsua_transport_config config;
1440 pjsip_host_port a_name;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001441 pjsip_tpfactory *tls;
Benny Prijonof3bbc132006-12-25 06:43:59 +00001442 pj_sockaddr_in local_addr;
1443
1444 /* Supply default config if it's not specified */
1445 if (cfg == NULL) {
1446 pjsua_transport_config_default(&config);
1447 config.port = 5061;
1448 cfg = &config;
1449 }
1450
1451 /* Init local address */
1452 pj_sockaddr_in_init(&local_addr, 0, 0);
1453
1454 if (cfg->port)
1455 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1456
1457 if (cfg->bound_addr.slen) {
1458 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1459 if (status != PJ_SUCCESS) {
1460 pjsua_perror(THIS_FILE,
1461 "Unable to resolve transport bound address",
1462 status);
1463 goto on_return;
1464 }
1465 }
1466
1467 /* Init published name */
1468 pj_bzero(&a_name, sizeof(pjsip_host_port));
1469 if (cfg->public_addr.slen)
1470 a_name.host = cfg->public_addr;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001471
1472 status = pjsip_tls_transport_start(pjsua_var.endpt,
Benny Prijonof3bbc132006-12-25 06:43:59 +00001473 &cfg->tls_setting,
1474 &local_addr, &a_name, 1, &tls);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001475 if (status != PJ_SUCCESS) {
1476 pjsua_perror(THIS_FILE, "Error creating SIP TLS listener",
1477 status);
1478 goto on_return;
1479 }
1480
1481 /* Save the transport */
1482 pjsua_var.tpdata[id].type = type;
1483 pjsua_var.tpdata[id].local_name = tls->addr_name;
1484 pjsua_var.tpdata[id].data.factory = tls;
1485#endif
1486
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001487 } else {
1488 status = PJSIP_EUNSUPTRANSPORT;
1489 pjsua_perror(THIS_FILE, "Error creating transport", status);
1490 goto on_return;
1491 }
1492
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001493
1494 /* Return the ID */
1495 if (p_id) *p_id = id;
1496
1497 status = PJ_SUCCESS;
1498
1499on_return:
1500
1501 PJSUA_UNLOCK();
1502
Benny Prijonod8410532006-06-15 11:04:33 +00001503 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001504}
1505
1506
1507/*
1508 * Register transport that has been created by application.
1509 */
1510PJ_DEF(pj_status_t) pjsua_transport_register( pjsip_transport *tp,
1511 pjsua_transport_id *p_id)
1512{
1513 unsigned id;
1514
1515 PJSUA_LOCK();
1516
1517 /* Find empty transport slot */
1518 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001519 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001520 break;
1521 }
1522
1523 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1524 pjsua_perror(THIS_FILE, "Error creating transport", PJ_ETOOMANY);
1525 PJSUA_UNLOCK();
1526 return PJ_ETOOMANY;
1527 }
1528
1529 /* Save the transport */
Benny Prijonod17a5e92007-05-29 11:51:45 +00001530 pjsua_var.tpdata[id].type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001531 pjsua_var.tpdata[id].local_name = tp->local_name;
1532 pjsua_var.tpdata[id].data.tp = tp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001533
1534 /* Return the ID */
1535 if (p_id) *p_id = id;
1536
1537 PJSUA_UNLOCK();
1538
1539 return PJ_SUCCESS;
1540}
1541
1542
1543/*
1544 * Enumerate all transports currently created in the system.
1545 */
1546PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[],
1547 unsigned *p_count )
1548{
1549 unsigned i, count;
1550
1551 PJSUA_LOCK();
1552
1553 for (i=0, count=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata) && count<*p_count;
1554 ++i)
1555 {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001556 if (!pjsua_var.tpdata[i].data.ptr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001557 continue;
1558
1559 id[count++] = i;
1560 }
1561
1562 *p_count = count;
1563
1564 PJSUA_UNLOCK();
1565
1566 return PJ_SUCCESS;
1567}
1568
1569
1570/*
1571 * Get information about transports.
1572 */
1573PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
1574 pjsua_transport_info *info)
1575{
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001576 pjsua_transport_data *t = &pjsua_var.tpdata[id];
Benny Prijono0a5cad82006-09-26 13:21:02 +00001577 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001578
Benny Prijonoac623b32006-07-03 15:19:31 +00001579 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001580
1581 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001582 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1583 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001584
1585 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001586 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001587
1588 PJSUA_LOCK();
1589
Benny Prijonoe93e2872006-06-28 16:46:49 +00001590 if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_UDP) {
1591
1592 pjsip_transport *tp = t->data.tp;
1593
1594 if (tp == NULL) {
1595 PJSUA_UNLOCK();
1596 return PJ_EINVALIDOP;
1597 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001598
Benny Prijonoe93e2872006-06-28 16:46:49 +00001599 info->id = id;
Benny Prijonod17a5e92007-05-29 11:51:45 +00001600 info->type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001601 info->type_name = pj_str(tp->type_name);
1602 info->info = pj_str(tp->info);
1603 info->flag = tp->flag;
1604 info->addr_len = tp->addr_len;
1605 info->local_addr = tp->local_addr;
1606 info->local_name = tp->local_name;
1607 info->usage_count = pj_atomic_get(tp->ref_cnt);
1608
Benny Prijono0a5cad82006-09-26 13:21:02 +00001609 status = PJ_SUCCESS;
1610
Benny Prijonoe93e2872006-06-28 16:46:49 +00001611 } else if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_TCP) {
1612
1613 pjsip_tpfactory *factory = t->data.factory;
1614
1615 if (factory == NULL) {
1616 PJSUA_UNLOCK();
1617 return PJ_EINVALIDOP;
1618 }
1619
1620 info->id = id;
1621 info->type = t->type;
1622 info->type_name = pj_str("TCP");
1623 info->info = pj_str("TCP transport");
1624 info->flag = factory->flag;
1625 info->addr_len = sizeof(factory->local_addr);
1626 info->local_addr = factory->local_addr;
1627 info->local_name = factory->addr_name;
1628 info->usage_count = 0;
1629
Benny Prijono0a5cad82006-09-26 13:21:02 +00001630 status = PJ_SUCCESS;
1631
1632 } else {
1633 pj_assert(!"Unsupported transport");
1634 status = PJ_EINVALIDOP;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001635 }
1636
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001637
1638 PJSUA_UNLOCK();
1639
Benny Prijono0a5cad82006-09-26 13:21:02 +00001640 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001641}
1642
1643
1644/*
1645 * Disable a transport or re-enable it.
1646 */
1647PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id,
1648 pj_bool_t enabled)
1649{
1650 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001651 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1652 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001653
1654 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001655 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001656
1657
1658 /* To be done!! */
1659 PJ_TODO(pjsua_transport_set_enable);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001660 PJ_UNUSED_ARG(enabled);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001661
1662 return PJ_EINVALIDOP;
1663}
1664
1665
1666/*
1667 * Close the transport.
1668 */
1669PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
1670 pj_bool_t force )
1671{
Benny Prijono5ff61872007-02-01 03:37:11 +00001672 pj_status_t status;
1673
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001674 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001675 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1676 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001677
1678 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001679 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001680
Benny Prijono0a5cad82006-09-26 13:21:02 +00001681 /* Note: destroy() may not work if there are objects still referencing
1682 * the transport.
1683 */
1684 if (force) {
1685 switch (pjsua_var.tpdata[id].type) {
1686 case PJSIP_TRANSPORT_UDP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001687 status = pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
1688 if (status != PJ_SUCCESS)
1689 return status;
1690 status = pjsip_transport_destroy(pjsua_var.tpdata[id].data.tp);
1691 if (status != PJ_SUCCESS)
1692 return status;
1693 break;
1694
1695 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001696 case PJSIP_TRANSPORT_TCP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001697 /* This will close the TCP listener, but existing TCP/TLS
1698 * connections (if any) will still linger
1699 */
1700 status = (*pjsua_var.tpdata[id].data.factory->destroy)
1701 (pjsua_var.tpdata[id].data.factory);
1702 if (status != PJ_SUCCESS)
1703 return status;
1704
Benny Prijono0a5cad82006-09-26 13:21:02 +00001705 break;
Benny Prijono5ff61872007-02-01 03:37:11 +00001706
Benny Prijono1ebd6142006-10-19 15:48:02 +00001707 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001708 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001709 }
1710
1711 } else {
Benny Prijono5ff61872007-02-01 03:37:11 +00001712 /* If force is not specified, transports will be closed at their
1713 * convenient time. However this will leak PJSUA-API transport
1714 * descriptors as PJSUA-API wouldn't know when exactly the
1715 * transport is closed thus it can't cleanup PJSUA transport
1716 * descriptor.
1717 */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001718 switch (pjsua_var.tpdata[id].type) {
1719 case PJSIP_TRANSPORT_UDP:
1720 return pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
Benny Prijono5ff61872007-02-01 03:37:11 +00001721 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001722 case PJSIP_TRANSPORT_TCP:
1723 return (*pjsua_var.tpdata[id].data.factory->destroy)
1724 (pjsua_var.tpdata[id].data.factory);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001725 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001726 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001727 }
1728 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001729
Benny Prijono5ff61872007-02-01 03:37:11 +00001730 /* Cleanup pjsua data when force is applied */
1731 if (force) {
1732 pjsua_var.tpdata[id].type = PJSIP_TRANSPORT_UNSPECIFIED;
1733 pjsua_var.tpdata[id].data.ptr = NULL;
1734 }
1735
1736 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001737}
1738
1739
1740/*
1741 * Add additional headers etc in msg_data specified by application
1742 * when sending requests.
1743 */
1744void pjsua_process_msg_data(pjsip_tx_data *tdata,
1745 const pjsua_msg_data *msg_data)
1746{
1747 pj_bool_t allow_body;
1748 const pjsip_hdr *hdr;
1749
Benny Prijono56315612006-07-18 14:39:40 +00001750 /* Always add User-Agent */
1751 if (pjsua_var.ua_cfg.user_agent.slen &&
1752 tdata->msg->type == PJSIP_REQUEST_MSG)
1753 {
1754 const pj_str_t STR_USER_AGENT = { "User-Agent", 10 };
1755 pjsip_hdr *h;
1756 h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool,
1757 &STR_USER_AGENT,
1758 &pjsua_var.ua_cfg.user_agent);
1759 pjsip_msg_add_hdr(tdata->msg, h);
1760 }
1761
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001762 if (!msg_data)
1763 return;
1764
1765 hdr = msg_data->hdr_list.next;
1766 while (hdr && hdr != &msg_data->hdr_list) {
1767 pjsip_hdr *new_hdr;
1768
Benny Prijonoa1e69682007-05-11 15:14:34 +00001769 new_hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001770 pjsip_msg_add_hdr(tdata->msg, new_hdr);
1771
1772 hdr = hdr->next;
1773 }
1774
1775 allow_body = (tdata->msg->body == NULL);
1776
1777 if (allow_body && msg_data->content_type.slen && msg_data->msg_body.slen) {
1778 pjsip_media_type ctype;
1779 pjsip_msg_body *body;
1780
1781 pjsua_parse_media_type(tdata->pool, &msg_data->content_type, &ctype);
1782 body = pjsip_msg_body_create(tdata->pool, &ctype.type, &ctype.subtype,
1783 &msg_data->msg_body);
1784 tdata->msg->body = body;
1785 }
1786}
1787
1788
1789/*
1790 * Add route_set to outgoing requests
1791 */
1792void pjsua_set_msg_route_set( pjsip_tx_data *tdata,
1793 const pjsip_route_hdr *route_set )
1794{
1795 const pjsip_route_hdr *r;
1796
1797 r = route_set->next;
1798 while (r != route_set) {
1799 pjsip_route_hdr *new_r;
1800
Benny Prijonoa1e69682007-05-11 15:14:34 +00001801 new_r = (pjsip_route_hdr*) pjsip_hdr_clone(tdata->pool, r);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001802 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)new_r);
1803
1804 r = r->next;
1805 }
1806}
1807
1808
1809/*
1810 * Simple version of MIME type parsing (it doesn't support parameters)
1811 */
1812void pjsua_parse_media_type( pj_pool_t *pool,
1813 const pj_str_t *mime,
1814 pjsip_media_type *media_type)
1815{
1816 pj_str_t tmp;
1817 char *pos;
1818
Benny Prijonoac623b32006-07-03 15:19:31 +00001819 pj_bzero(media_type, sizeof(*media_type));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001820
1821 pj_strdup_with_null(pool, &tmp, mime);
1822
1823 pos = pj_strchr(&tmp, '/');
1824 if (pos) {
1825 media_type->type.ptr = tmp.ptr;
1826 media_type->type.slen = (pos-tmp.ptr);
1827 media_type->subtype.ptr = pos+1;
1828 media_type->subtype.slen = tmp.ptr+tmp.slen-pos-1;
1829 } else {
1830 media_type->type = tmp;
1831 }
1832}
1833
1834
1835/*
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001836 * Internal function to init transport selector from transport id.
1837 */
1838void pjsua_init_tpselector(pjsua_transport_id tp_id,
1839 pjsip_tpselector *sel)
1840{
1841 pjsua_transport_data *tpdata;
1842 unsigned flag;
1843
1844 pj_bzero(sel, sizeof(*sel));
1845 if (tp_id == PJSUA_INVALID_ID)
1846 return;
1847
Benny Prijonoa1e69682007-05-11 15:14:34 +00001848 pj_assert(tp_id >= 0 && tp_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata));
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001849 tpdata = &pjsua_var.tpdata[tp_id];
1850
1851 flag = pjsip_transport_get_flag_from_type(tpdata->type);
1852
1853 if (flag & PJSIP_TRANSPORT_DATAGRAM) {
1854 sel->type = PJSIP_TPSELECTOR_TRANSPORT;
1855 sel->u.transport = tpdata->data.tp;
1856 } else {
1857 sel->type = PJSIP_TPSELECTOR_LISTENER;
1858 sel->u.listener = tpdata->data.factory;
1859 }
1860}
1861
1862
Benny Prijono6ba8c542007-10-16 01:34:14 +00001863/* Callback upon NAT detection completion */
1864static void nat_detect_cb(void *user_data,
1865 const pj_stun_nat_detect_result *res)
1866{
1867 PJ_UNUSED_ARG(user_data);
1868
1869 pjsua_var.nat_in_progress = PJ_FALSE;
1870 pjsua_var.nat_status = res->status;
1871 pjsua_var.nat_type = res->nat_type;
1872
1873 if (pjsua_var.ua_cfg.cb.on_nat_detect) {
1874 (*pjsua_var.ua_cfg.cb.on_nat_detect)(res);
1875 }
1876}
1877
1878
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001879/*
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001880 * Detect NAT type.
1881 */
Benny Prijono6ba8c542007-10-16 01:34:14 +00001882PJ_DEF(pj_status_t) pjsua_detect_nat_type()
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001883{
1884 pj_status_t status;
1885
Benny Prijono6ba8c542007-10-16 01:34:14 +00001886 if (pjsua_var.nat_in_progress)
1887 return PJ_SUCCESS;
1888
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001889 /* Make sure STUN server resolution has completed */
1890 status = pjsua_resolve_stun_server(PJ_TRUE);
1891 if (status != PJ_SUCCESS) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00001892 pjsua_var.nat_status = status;
1893 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001894 return status;
1895 }
1896
1897 /* Make sure we have STUN */
1898 if (pjsua_var.stun_srv.ipv4.sin_family == 0) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00001899 pjsua_var.nat_status = PJNATH_ESTUNINSERVER;
1900 return PJNATH_ESTUNINSERVER;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001901 }
1902
Benny Prijono6ba8c542007-10-16 01:34:14 +00001903 status = pj_stun_detect_nat_type(&pjsua_var.stun_srv.ipv4,
1904 &pjsua_var.stun_cfg,
1905 NULL, &nat_detect_cb);
1906
1907 if (status != PJ_SUCCESS) {
1908 pjsua_var.nat_status = status;
1909 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
1910 return status;
1911 }
1912
1913 pjsua_var.nat_in_progress = PJ_TRUE;
1914
1915 return PJ_SUCCESS;
1916}
1917
1918
1919/*
1920 * Get NAT type.
1921 */
1922PJ_DEF(pj_status_t) pjsua_get_nat_type(pj_stun_nat_type *type)
1923{
1924 *type = pjsua_var.nat_type;
1925 return pjsua_var.nat_status;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001926}
1927
1928
1929/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001930 * Verify that valid SIP url is given.
1931 */
1932PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url)
1933{
1934 pjsip_uri *p;
1935 pj_pool_t *pool;
1936 char *url;
1937 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
1938
1939 if (!len) return -1;
1940
1941 pool = pj_pool_create(&pjsua_var.cp.factory, "check%p", 1024, 0, NULL);
1942 if (!pool) return -1;
1943
Benny Prijonoa1e69682007-05-11 15:14:34 +00001944 url = (char*) pj_pool_alloc(pool, len+1);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001945 pj_ansi_strcpy(url, c_url);
1946
1947 p = pjsip_parse_uri(pool, url, len, 0);
Benny Prijonocf0b4b22007-10-06 17:31:09 +00001948 if (!p || (pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0 &&
1949 pj_stricmp2(pjsip_uri_get_scheme(p), "sips") != 0))
1950 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001951 p = NULL;
Benny Prijonocf0b4b22007-10-06 17:31:09 +00001952 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001953
1954 pj_pool_release(pool);
1955 return p ? 0 : -1;
1956}
Benny Prijonoda9785b2007-04-02 20:43:06 +00001957
1958
1959/*
1960 * This is a utility function to dump the stack states to log, using
1961 * verbosity level 3.
1962 */
1963PJ_DEF(void) pjsua_dump(pj_bool_t detail)
1964{
1965 unsigned old_decor;
1966 unsigned i;
1967 char buf[1024];
1968
1969 PJ_LOG(3,(THIS_FILE, "Start dumping application states:"));
1970
1971 old_decor = pj_log_get_decor();
1972 pj_log_set_decor(old_decor & (PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
1973
1974 if (detail)
1975 pj_dump_config();
1976
1977 pjsip_endpt_dump(pjsua_get_pjsip_endpt(), detail);
1978
1979 pjmedia_endpt_dump(pjsua_get_pjmedia_endpt());
1980
1981 PJ_LOG(3,(THIS_FILE, "Dumping media transports:"));
1982 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1983 pjsua_call *call = &pjsua_var.calls[i];
1984 pjmedia_sock_info skinfo;
1985
Benny Prijono4f093d22007-04-09 08:54:32 +00001986 /* MSVC complains about skinfo not being initialized */
1987 pj_bzero(&skinfo, sizeof(skinfo));
1988
Benny Prijonoda9785b2007-04-02 20:43:06 +00001989 pjmedia_transport_get_info(call->med_tp, &skinfo);
1990
1991 PJ_LOG(3,(THIS_FILE, " %s: %s:%d",
1992 (pjsua_var.media_cfg.enable_ice ? "ICE" : "UDP"),
1993 pj_inet_ntoa(skinfo.rtp_addr_name.sin_addr),
1994 (int)pj_ntohs(skinfo.rtp_addr_name.sin_port)));
1995 }
1996
1997 pjsip_tsx_layer_dump(detail);
1998 pjsip_ua_dump(detail);
1999
2000
2001 /* Dump all invite sessions: */
2002 PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
2003
2004 if (pjsua_call_get_count() == 0) {
2005
2006 PJ_LOG(3,(THIS_FILE, " - no sessions -"));
2007
2008 } else {
2009 unsigned i;
2010
2011 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2012 if (pjsua_call_is_active(i)) {
2013 pjsua_call_dump(i, detail, buf, sizeof(buf), " ");
2014 PJ_LOG(3,(THIS_FILE, "%s", buf));
2015 }
2016 }
2017 }
2018
2019 /* Dump presence status */
2020 pjsua_pres_dump(detail);
2021
2022 pj_log_set_decor(old_decor);
2023 PJ_LOG(3,(THIS_FILE, "Dump complete"));
2024}
2025