blob: 384e332bb9d89bafcb58f871c37954d8f6b62ee5 [file] [log] [blame]
Benny Prijono268ca612006-02-07 12:34:11 +00001/* $Id$ */
2/*
Benny Prijono32177c02008-06-20 22:44:47 +00003 * Copyright (C) 2003-2008 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 Prijono44e88ea2007-10-30 15:42:35 +000030PJ_DEF(struct pjsua_data*) pjsua_get_var(void)
31{
32 return &pjsua_var;
33}
34
35
Benny Prijonoeebe9af2006-06-13 22:57:13 +000036/* Display error */
37PJ_DEF(void) pjsua_perror( const char *sender, const char *title,
38 pj_status_t status)
Benny Prijono268ca612006-02-07 12:34:11 +000039{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000040 char errmsg[PJ_ERR_MSG_SIZE];
Benny Prijonoa91a0032006-02-26 21:23:45 +000041
Benny Prijonoeebe9af2006-06-13 22:57:13 +000042 pj_strerror(status, errmsg, sizeof(errmsg));
43 PJ_LOG(3,(sender, "%s: %s [status=%d]", title, errmsg, status));
Benny Prijono268ca612006-02-07 12:34:11 +000044}
45
46
Benny Prijonoeebe9af2006-06-13 22:57:13 +000047static void init_data()
Benny Prijonodc39fe82006-05-26 12:17:46 +000048{
49 unsigned i;
50
Benny Prijonoc97608e2007-03-23 16:34:20 +000051 pj_bzero(&pjsua_var, sizeof(pjsua_var));
52
Benny Prijonoeebe9af2006-06-13 22:57:13 +000053 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i)
54 pjsua_var.acc[i].index = i;
55
56 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i)
57 pjsua_var.tpdata[i].index = i;
Benny Prijonoc97608e2007-03-23 16:34:20 +000058
59 pjsua_var.stun_status = PJ_EUNKNOWN;
Benny Prijono6ba8c542007-10-16 01:34:14 +000060 pjsua_var.nat_status = PJ_EPENDING;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000061}
Benny Prijonodc39fe82006-05-26 12:17:46 +000062
63
Benny Prijono1f61a8f2007-08-16 10:11:44 +000064PJ_DEF(void) pjsua_logging_config_default(pjsua_logging_config *cfg)
65{
66 pj_bzero(cfg, sizeof(*cfg));
67
68 cfg->msg_logging = PJ_TRUE;
69 cfg->level = 5;
70 cfg->console_level = 4;
71 cfg->decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME |
72 PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE;
73}
74
75PJ_DEF(void) pjsua_logging_config_dup(pj_pool_t *pool,
76 pjsua_logging_config *dst,
77 const pjsua_logging_config *src)
78{
79 pj_memcpy(dst, src, sizeof(*src));
80 pj_strdup_with_null(pool, &dst->log_filename, &src->log_filename);
81}
82
83PJ_DEF(void) pjsua_config_default(pjsua_config *cfg)
84{
85 pj_bzero(cfg, sizeof(*cfg));
86
87 cfg->max_calls = 4;
88 cfg->thread_cnt = 1;
Benny Prijono91a6a172007-10-31 08:59:29 +000089 cfg->nat_type_in_sdp = 1;
Benny Prijonod8179652008-01-23 20:39:07 +000090#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
91 cfg->use_srtp = PJSUA_DEFAULT_USE_SRTP;
92 cfg->srtp_secure_signaling = PJSUA_DEFAULT_SRTP_SECURE_SIGNALING;
93#endif
Benny Prijono1f61a8f2007-08-16 10:11:44 +000094}
95
Benny Prijono1f61a8f2007-08-16 10:11:44 +000096PJ_DEF(void) pjsua_config_dup(pj_pool_t *pool,
97 pjsua_config *dst,
98 const pjsua_config *src)
99{
100 unsigned i;
101
102 pj_memcpy(dst, src, sizeof(*src));
103
104 for (i=0; i<src->outbound_proxy_cnt; ++i) {
105 pj_strdup_with_null(pool, &dst->outbound_proxy[i],
106 &src->outbound_proxy[i]);
107 }
108
109 for (i=0; i<src->cred_count; ++i) {
110 pjsip_cred_dup(pool, &dst->cred_info[i], &src->cred_info[i]);
111 }
112
113 pj_strdup_with_null(pool, &dst->user_agent, &src->user_agent);
114 pj_strdup_with_null(pool, &dst->stun_domain, &src->stun_domain);
115 pj_strdup_with_null(pool, &dst->stun_host, &src->stun_host);
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000116}
117
118PJ_DEF(void) pjsua_msg_data_init(pjsua_msg_data *msg_data)
119{
120 pj_bzero(msg_data, sizeof(*msg_data));
121 pj_list_init(&msg_data->hdr_list);
122}
123
124PJ_DEF(void) pjsua_transport_config_default(pjsua_transport_config *cfg)
125{
126 pj_bzero(cfg, sizeof(*cfg));
127 pjsip_tls_setting_default(&cfg->tls_setting);
128}
129
130PJ_DEF(void) pjsua_transport_config_dup(pj_pool_t *pool,
131 pjsua_transport_config *dst,
132 const pjsua_transport_config *src)
133{
134 PJ_UNUSED_ARG(pool);
135 pj_memcpy(dst, src, sizeof(*src));
136}
137
138PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg)
139{
140 pj_bzero(cfg, sizeof(*cfg));
141
142 cfg->reg_timeout = PJSUA_REG_INTERVAL;
143 cfg->transport_id = PJSUA_INVALID_ID;
Benny Prijonoe8554ef2008-03-22 09:33:52 +0000144 cfg->allow_contact_rewrite = PJ_TRUE;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000145 cfg->require_100rel = pjsua_var.ua_cfg.require_100rel;
Benny Prijonobddef2c2007-10-31 13:28:08 +0000146 cfg->ka_interval = 15;
147 cfg->ka_data = pj_str("\r\n");
Benny Prijonod8179652008-01-23 20:39:07 +0000148#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
149 cfg->use_srtp = pjsua_var.ua_cfg.use_srtp;
150 cfg->srtp_secure_signaling = pjsua_var.ua_cfg.srtp_secure_signaling;
151#endif
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000152}
153
154PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg)
155{
156 pj_bzero(cfg, sizeof(*cfg));
157}
158
159PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg)
160{
161 pj_bzero(cfg, sizeof(*cfg));
162
163 cfg->clock_rate = PJSUA_DEFAULT_CLOCK_RATE;
Benny Prijono50f19b32008-03-11 13:15:43 +0000164 cfg->snd_clock_rate = 0;
Benny Prijono7d60d052008-03-29 12:24:20 +0000165 cfg->channel_count = 1;
Benny Prijono37c710b2008-01-10 12:09:26 +0000166 cfg->audio_frame_ptime = PJSUA_DEFAULT_AUDIO_FRAME_PTIME;
167 cfg->max_media_ports = PJSUA_MAX_CONF_PORTS;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000168 cfg->has_ioqueue = PJ_TRUE;
169 cfg->thread_cnt = 1;
170 cfg->quality = PJSUA_DEFAULT_CODEC_QUALITY;
171 cfg->ilbc_mode = PJSUA_DEFAULT_ILBC_MODE;
172 cfg->ec_tail_len = PJSUA_DEFAULT_EC_TAIL_LEN;
173 cfg->jb_init = cfg->jb_min_pre = cfg->jb_max_pre = cfg->jb_max = -1;
Nanang Izzuddin68559c32008-06-13 17:01:46 +0000174 cfg->snd_auto_close_time = -1;
Benny Prijonof76e1392008-06-06 14:51:48 +0000175
176 cfg->turn_conn_type = PJ_TURN_TP_UDP;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000177}
178
Benny Prijonodc39fe82006-05-26 12:17:46 +0000179
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000180/*****************************************************************************
181 * This is a very simple PJSIP module, whose sole purpose is to display
182 * incoming and outgoing messages to log. This module will have priority
183 * higher than transport layer, which means:
184 *
185 * - incoming messages will come to this module first before reaching
186 * transaction layer.
187 *
188 * - outgoing messages will come to this module last, after the message
189 * has been 'printed' to contiguous buffer by transport layer and
190 * appropriate transport instance has been decided for this message.
191 *
192 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000193
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000194/* Notification on incoming messages */
195static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
196{
Benny Prijonob4a17c92006-07-10 14:40:21 +0000197 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
198 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000199 "--end msg--",
200 rdata->msg_info.len,
201 pjsip_rx_data_get_info(rdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000202 rdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000203 rdata->pkt_info.src_name,
204 rdata->pkt_info.src_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000205 (int)rdata->msg_info.len,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000206 rdata->msg_info.msg_buf));
207
208 /* Always return false, otherwise messages will not get processed! */
209 return PJ_FALSE;
210}
Benny Prijonodc39fe82006-05-26 12:17:46 +0000211
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000212/* Notification on outgoing messages */
213static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
214{
215
216 /* Important note:
217 * tp_info field is only valid after outgoing messages has passed
218 * transport layer. So don't try to access tp_info when the module
219 * has lower priority than transport layer.
220 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000221
Benny Prijonob4a17c92006-07-10 14:40:21 +0000222 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
223 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000224 "--end msg--",
225 (tdata->buf.cur - tdata->buf.start),
226 pjsip_tx_data_get_info(tdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000227 tdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000228 tdata->tp_info.dst_name,
229 tdata->tp_info.dst_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000230 (int)(tdata->buf.cur - tdata->buf.start),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000231 tdata->buf.start));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000232
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000233 /* Always return success, otherwise message will not get sent! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000234 return PJ_SUCCESS;
235}
236
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000237/* The module instance. */
238static pjsip_module pjsua_msg_logger =
239{
240 NULL, NULL, /* prev, next. */
241 { "mod-pjsua-log", 13 }, /* Name. */
242 -1, /* Id */
243 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
244 NULL, /* load() */
245 NULL, /* start() */
246 NULL, /* stop() */
247 NULL, /* unload() */
248 &logging_on_rx_msg, /* on_rx_request() */
249 &logging_on_rx_msg, /* on_rx_response() */
250 &logging_on_tx_msg, /* on_tx_request. */
251 &logging_on_tx_msg, /* on_tx_response() */
252 NULL, /* on_tsx_state() */
253
254};
255
256
257/*****************************************************************************
Benny Prijono56315612006-07-18 14:39:40 +0000258 * Another simple module to handle incoming OPTIONS request
259 */
260
261/* Notification on incoming request */
262static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
263{
264 pjsip_tx_data *tdata;
265 pjsip_response_addr res_addr;
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000266 pjmedia_transport_info tpinfo;
Benny Prijono56315612006-07-18 14:39:40 +0000267 pjmedia_sdp_session *sdp;
268 const pjsip_hdr *cap_hdr;
269 pj_status_t status;
270
271 /* Only want to handle OPTIONS requests */
272 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000273 pjsip_get_options_method()) != 0)
Benny Prijono56315612006-07-18 14:39:40 +0000274 {
275 return PJ_FALSE;
276 }
277
278 /* Create basic response. */
279 status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL,
280 &tdata);
281 if (status != PJ_SUCCESS) {
282 pjsua_perror(THIS_FILE, "Unable to create OPTIONS response", status);
283 return PJ_TRUE;
284 }
285
286 /* Add Allow header */
287 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ALLOW, NULL);
288 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000289 pjsip_msg_add_hdr(tdata->msg,
290 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000291 }
292
293 /* Add Accept header */
294 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL);
295 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000296 pjsip_msg_add_hdr(tdata->msg,
297 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000298 }
299
300 /* Add Supported header */
301 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_SUPPORTED, NULL);
302 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000303 pjsip_msg_add_hdr(tdata->msg,
304 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000305 }
306
307 /* Add Allow-Events header from the evsub module */
308 cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL);
309 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000310 pjsip_msg_add_hdr(tdata->msg,
311 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000312 }
313
314 /* Add User-Agent header */
315 if (pjsua_var.ua_cfg.user_agent.slen) {
316 const pj_str_t USER_AGENT = { "User-Agent", 10};
317 pjsip_hdr *h;
318
319 h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
320 &USER_AGENT,
321 &pjsua_var.ua_cfg.user_agent);
322 pjsip_msg_add_hdr(tdata->msg, h);
323 }
324
Benny Prijono617c5bc2007-04-02 19:51:21 +0000325 /* Get media socket info */
Benny Prijono734fc2d2008-03-17 16:05:35 +0000326 pjmedia_transport_info_init(&tpinfo);
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000327 pjmedia_transport_get_info(pjsua_var.calls[0].med_tp, &tpinfo);
Benny Prijono617c5bc2007-04-02 19:51:21 +0000328
Benny Prijono56315612006-07-18 14:39:40 +0000329 /* Add SDP body, using call0's RTP address */
330 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool, 1,
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000331 &tpinfo.sock_info, &sdp);
Benny Prijono56315612006-07-18 14:39:40 +0000332 if (status == PJ_SUCCESS) {
333 pjsip_create_sdp_body(tdata->pool, sdp, &tdata->msg->body);
334 }
335
336 /* Send response statelessly */
337 pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
338 status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, tdata, NULL, NULL);
339 if (status != PJ_SUCCESS)
340 pjsip_tx_data_dec_ref(tdata);
341
342 return PJ_TRUE;
343}
344
345
346/* The module instance. */
347static pjsip_module pjsua_options_handler =
348{
349 NULL, NULL, /* prev, next. */
350 { "mod-pjsua-options", 17 }, /* Name. */
351 -1, /* Id */
352 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
353 NULL, /* load() */
354 NULL, /* start() */
355 NULL, /* stop() */
356 NULL, /* unload() */
357 &options_on_rx_request, /* on_rx_request() */
358 NULL, /* on_rx_response() */
359 NULL, /* on_tx_request. */
360 NULL, /* on_tx_response() */
361 NULL, /* on_tsx_state() */
362
363};
364
365
366/*****************************************************************************
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000367 * These two functions are the main callbacks registered to PJSIP stack
368 * to receive SIP request and response messages that are outside any
369 * dialogs and any transactions.
370 */
Benny Prijono268ca612006-02-07 12:34:11 +0000371
372/*
373 * Handler for receiving incoming requests.
374 *
375 * This handler serves multiple purposes:
376 * - it receives requests outside dialogs.
377 * - it receives requests inside dialogs, when the requests are
378 * unhandled by other dialog usages. Example of these
379 * requests are: MESSAGE.
380 */
381static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
382{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000383 pj_bool_t processed = PJ_FALSE;
384
385 PJSUA_LOCK();
Benny Prijono38998232006-02-08 22:44:25 +0000386
Benny Prijono84126ab2006-02-09 09:30:09 +0000387 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
Benny Prijono38998232006-02-08 22:44:25 +0000388
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000389 processed = pjsua_call_on_incoming(rdata);
Benny Prijono38998232006-02-08 22:44:25 +0000390 }
391
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000392 PJSUA_UNLOCK();
393
394 return processed;
Benny Prijono268ca612006-02-07 12:34:11 +0000395}
396
397
398/*
399 * Handler for receiving incoming responses.
400 *
401 * This handler serves multiple purposes:
402 * - it receives strayed responses (i.e. outside any dialog and
403 * outside any transactions).
404 * - it receives responses coming to a transaction, when pjsua
405 * module is set as transaction user for the transaction.
406 * - it receives responses inside a dialog, when these responses
407 * are unhandled by other dialog usages.
408 */
409static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
410{
411 PJ_UNUSED_ARG(rdata);
Benny Prijono268ca612006-02-07 12:34:11 +0000412 return PJ_FALSE;
413}
414
415
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000416/*****************************************************************************
417 * Logging.
418 */
419
420/* Log callback */
421static void log_writer(int level, const char *buffer, int len)
Benny Prijono268ca612006-02-07 12:34:11 +0000422{
Benny Prijono572d4852006-11-23 21:50:02 +0000423 /* Write to file, stdout or application callback. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000424
425 if (pjsua_var.log_file) {
426 pj_ssize_t size = len;
427 pj_file_write(pjsua_var.log_file, buffer, &size);
Benny Prijono980ca0a2007-04-03 17:55:08 +0000428 /* This will slow things down considerably! Don't do it!
429 pj_file_flush(pjsua_var.log_file);
430 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000431 }
432
Benny Prijono572d4852006-11-23 21:50:02 +0000433 if (level <= (int)pjsua_var.log_cfg.console_level) {
434 if (pjsua_var.log_cfg.cb)
435 (*pjsua_var.log_cfg.cb)(level, buffer, len);
436 else
437 pj_log_write(level, buffer, len);
438 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000439}
440
441
442/*
443 * Application can call this function at any time (after pjsua_create(), of
444 * course) to change logging settings.
445 */
446PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg)
447{
448 pj_status_t status;
449
450 /* Save config. */
451 pjsua_logging_config_dup(pjsua_var.pool, &pjsua_var.log_cfg, cfg);
452
453 /* Redirect log function to ours */
454 pj_log_set_log_func( &log_writer );
455
Benny Prijono9cb09a22007-08-30 09:35:10 +0000456 /* Set decor */
457 pj_log_set_decor(pjsua_var.log_cfg.decor);
458
Benny Prijono4190cf92008-01-18 13:25:05 +0000459 /* Set log level */
460 pj_log_set_level(pjsua_var.log_cfg.level);
461
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000462 /* Close existing file, if any */
463 if (pjsua_var.log_file) {
464 pj_file_close(pjsua_var.log_file);
465 pjsua_var.log_file = NULL;
466 }
467
468 /* If output log file is desired, create the file: */
469 if (pjsua_var.log_cfg.log_filename.slen) {
470
471 status = pj_file_open(pjsua_var.pool,
472 pjsua_var.log_cfg.log_filename.ptr,
473 PJ_O_WRONLY,
474 &pjsua_var.log_file);
475
476 if (status != PJ_SUCCESS) {
477 pjsua_perror(THIS_FILE, "Error creating log file", status);
478 return status;
479 }
480 }
481
482 /* Unregister msg logging if it's previously registered */
483 if (pjsua_msg_logger.id >= 0) {
484 pjsip_endpt_unregister_module(pjsua_var.endpt, &pjsua_msg_logger);
485 pjsua_msg_logger.id = -1;
486 }
487
488 /* Enable SIP message logging */
489 if (pjsua_var.log_cfg.msg_logging)
490 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_msg_logger);
491
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000492 return PJ_SUCCESS;
493}
494
495
496/*****************************************************************************
497 * PJSUA Base API.
498 */
499
500/* Worker thread function. */
501static int worker_thread(void *arg)
502{
503 enum { TIMEOUT = 10 };
Benny Prijonof8baa872006-02-27 23:54:23 +0000504
Benny Prijono268ca612006-02-07 12:34:11 +0000505 PJ_UNUSED_ARG(arg);
506
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000507 while (!pjsua_var.thread_quit_flag) {
508 int count;
509
510 count = pjsua_handle_events(TIMEOUT);
511 if (count < 0)
512 pj_thread_sleep(TIMEOUT);
513 }
Benny Prijono268ca612006-02-07 12:34:11 +0000514
515 return 0;
516}
517
Benny Prijonodc39fe82006-05-26 12:17:46 +0000518
Benny Prijono8389c312008-02-21 21:36:34 +0000519/* Init random seed */
520static void init_random_seed(void)
521{
522 pj_sockaddr addr;
523 const pj_str_t *hostname;
524 pj_uint32_t pid;
525 pj_time_val t;
526 unsigned seed=0;
527
528 /* Add hostname */
529 hostname = pj_gethostname();
530 seed = pj_hash_calc(seed, hostname->ptr, (int)hostname->slen);
531
532 /* Add primary IP address */
533 if (pj_gethostip(pj_AF_INET(), &addr)==PJ_SUCCESS)
534 seed = pj_hash_calc(seed, &addr.ipv4.sin_addr, 4);
535
536 /* Get timeofday */
537 pj_gettimeofday(&t);
538 seed = pj_hash_calc(seed, &t, sizeof(t));
539
540 /* Add PID */
541 pid = pj_getpid();
542 seed = pj_hash_calc(seed, &pid, sizeof(pid));
543
544 /* Init random seed */
545 pj_srand(seed);
546}
547
Benny Prijono268ca612006-02-07 12:34:11 +0000548/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000549 * Instantiate pjsua application.
Benny Prijonodc39fe82006-05-26 12:17:46 +0000550 */
551PJ_DEF(pj_status_t) pjsua_create(void)
552{
553 pj_status_t status;
Benny Prijono268ca612006-02-07 12:34:11 +0000554
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000555 /* Init pjsua data */
556 init_data();
Benny Prijono268ca612006-02-07 12:34:11 +0000557
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000558 /* Set default logging settings */
559 pjsua_logging_config_default(&pjsua_var.log_cfg);
560
561 /* Init PJLIB: */
Benny Prijono268ca612006-02-07 12:34:11 +0000562 status = pj_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000563 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
564
Benny Prijono8389c312008-02-21 21:36:34 +0000565 /* Init random seed */
566 init_random_seed();
Benny Prijono268ca612006-02-07 12:34:11 +0000567
Benny Prijonofccab712006-02-22 22:23:22 +0000568 /* Init PJLIB-UTIL: */
Benny Prijonofccab712006-02-22 22:23:22 +0000569 status = pjlib_util_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000570 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonofccab712006-02-22 22:23:22 +0000571
Benny Prijonoec921342007-03-24 13:00:30 +0000572 /* Init PJNATH */
573 status = pjnath_init();
574 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono268ca612006-02-07 12:34:11 +0000575
Benny Prijono094d3ad2006-11-21 08:41:00 +0000576 /* Set default sound device ID */
577 pjsua_var.cap_dev = pjsua_var.play_dev = -1;
578
Benny Prijono268ca612006-02-07 12:34:11 +0000579 /* Init caching pool. */
Benny Prijono106f5b72007-08-30 13:49:33 +0000580 pj_caching_pool_init(&pjsua_var.cp, NULL, 0);
Benny Prijono268ca612006-02-07 12:34:11 +0000581
582 /* Create memory pool for application. */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000583 pjsua_var.pool = pjsua_pool_create("pjsua", 1000, 1000);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000584
585 PJ_ASSERT_RETURN(pjsua_var.pool, PJ_ENOMEM);
Benny Prijono268ca612006-02-07 12:34:11 +0000586
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000587 /* Create mutex */
588 status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua",
589 &pjsua_var.mutex);
Benny Prijonoccf95622006-02-07 18:48:01 +0000590 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000591 pjsua_perror(THIS_FILE, "Unable to create mutex", status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000592 return status;
593 }
594
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000595 /* Must create SIP endpoint to initialize SIP parser. The parser
596 * is needed for example when application needs to call pjsua_verify_url().
Benny Prijonob8c25182006-04-29 08:31:09 +0000597 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000598 status = pjsip_endpt_create(&pjsua_var.cp.factory,
599 pj_gethostname()->ptr,
600 &pjsua_var.endpt);
601 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono39879152006-02-23 02:09:10 +0000602
603
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000604 return PJ_SUCCESS;
605}
606
607
608/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000609 * Initialize pjsua with the specified settings. All the settings are
610 * optional, and the default values will be used when the config is not
611 * specified.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000612 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000613PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
614 const pjsua_logging_config *log_cfg,
615 const pjsua_media_config *media_cfg)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000616{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000617 pjsua_config default_cfg;
618 pjsua_media_config default_media_cfg;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000619 const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
Benny Prijonodc39fe82006-05-26 12:17:46 +0000620 pj_status_t status;
621
622
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000623 /* Create default configurations when the config is not supplied */
624
625 if (ua_cfg == NULL) {
626 pjsua_config_default(&default_cfg);
627 ua_cfg = &default_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000628 }
629
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000630 if (media_cfg == NULL) {
631 pjsua_media_config_default(&default_media_cfg);
632 media_cfg = &default_media_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000633 }
634
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000635 /* Initialize logging first so that info/errors can be captured */
636 if (log_cfg) {
637 status = pjsua_reconfigure_logging(log_cfg);
638 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000639 }
640
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000641 /* If nameserver is configured, create DNS resolver instance and
642 * set it to be used by SIP resolver.
643 */
644 if (ua_cfg->nameserver_count) {
645#if PJSIP_HAS_RESOLVER
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000646 unsigned i;
647
648 /* Create DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000649 status = pjsip_endpt_create_resolver(pjsua_var.endpt,
650 &pjsua_var.resolver);
Benny Prijono318f3842007-05-15 20:27:08 +0000651 if (status != PJ_SUCCESS) {
652 pjsua_perror(THIS_FILE, "Error creating resolver", status);
653 return status;
654 }
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000655
656 /* Configure nameserver for the DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000657 status = pj_dns_resolver_set_ns(pjsua_var.resolver,
658 ua_cfg->nameserver_count,
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000659 ua_cfg->nameserver, NULL);
660 if (status != PJ_SUCCESS) {
661 pjsua_perror(THIS_FILE, "Error setting nameserver", status);
662 return status;
663 }
664
665 /* Set this DNS resolver to be used by the SIP resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000666 status = pjsip_endpt_set_resolver(pjsua_var.endpt, pjsua_var.resolver);
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000667 if (status != PJ_SUCCESS) {
668 pjsua_perror(THIS_FILE, "Error setting DNS resolver", status);
669 return status;
670 }
671
672 /* Print nameservers */
673 for (i=0; i<ua_cfg->nameserver_count; ++i) {
674 PJ_LOG(4,(THIS_FILE, "Nameserver %.*s added",
675 (int)ua_cfg->nameserver[i].slen,
676 ua_cfg->nameserver[i].ptr));
677 }
678#else
679 PJ_LOG(2,(THIS_FILE,
680 "DNS resolver is disabled (PJSIP_HAS_RESOLVER==0)"));
681#endif
682 }
683
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000684 /* Init SIP UA: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000685
686 /* Initialize transaction layer: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000687 status = pjsip_tsx_layer_init_module(pjsua_var.endpt);
688 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000689
Benny Prijonodc39fe82006-05-26 12:17:46 +0000690
691 /* Initialize UA layer module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000692 status = pjsip_ua_init_module( pjsua_var.endpt, NULL );
693 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000694
Benny Prijonodc39fe82006-05-26 12:17:46 +0000695
Benny Prijono053f5222006-11-11 16:16:04 +0000696 /* Initialize Replaces support. */
697 status = pjsip_replaces_init_module( pjsua_var.endpt );
698 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
699
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000700 /* Initialize 100rel support */
701 status = pjsip_100rel_init_module(pjsua_var.endpt);
702 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono053f5222006-11-11 16:16:04 +0000703
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000704 /* Initialize and register PJSUA application module. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000705 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000706 const pjsip_module mod_initializer =
Benny Prijonodc39fe82006-05-26 12:17:46 +0000707 {
708 NULL, NULL, /* prev, next. */
709 { "mod-pjsua", 9 }, /* Name. */
710 -1, /* Id */
711 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
712 NULL, /* load() */
713 NULL, /* start() */
714 NULL, /* stop() */
715 NULL, /* unload() */
716 &mod_pjsua_on_rx_request, /* on_rx_request() */
717 &mod_pjsua_on_rx_response, /* on_rx_response() */
718 NULL, /* on_tx_request. */
719 NULL, /* on_tx_response() */
720 NULL, /* on_tsx_state() */
721 };
722
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000723 pjsua_var.mod = mod_initializer;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000724
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000725 status = pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_var.mod);
726 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000727 }
728
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000729
Benny Prijonodc39fe82006-05-26 12:17:46 +0000730
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000731 /* Initialize PJSUA call subsystem: */
732 status = pjsua_call_subsys_init(ua_cfg);
733 if (status != PJ_SUCCESS)
Benny Prijonodc39fe82006-05-26 12:17:46 +0000734 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000735
736
Benny Prijonoc97608e2007-03-23 16:34:20 +0000737 /* Start resolving STUN server */
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000738
Benny Prijonoc97608e2007-03-23 16:34:20 +0000739 status = pjsua_resolve_stun_server(PJ_FALSE);
740 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
741 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
742 return status;
743 }
744
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000745 /* Initialize PJSUA media subsystem */
746 status = pjsua_media_subsys_init(media_cfg);
747 if (status != PJ_SUCCESS)
748 goto on_error;
749
Benny Prijonodc39fe82006-05-26 12:17:46 +0000750
751 /* Init core SIMPLE module : */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000752 status = pjsip_evsub_init_module(pjsua_var.endpt);
753 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000754
Benny Prijonodc39fe82006-05-26 12:17:46 +0000755
756 /* Init presence module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000757 status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance());
758 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000759
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000760 /* Init PUBLISH module */
761 pjsip_publishc_init_module(pjsua_var.endpt);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000762
763 /* Init xfer/REFER module */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000764 status = pjsip_xfer_init_module( pjsua_var.endpt );
765 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000766
767 /* Init pjsua presence handler: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000768 status = pjsua_pres_init();
769 if (status != PJ_SUCCESS)
770 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000771
772 /* Init out-of-dialog MESSAGE request handler. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000773 status = pjsua_im_init();
774 if (status != PJ_SUCCESS)
775 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000776
Benny Prijonoc8141a82006-08-20 09:12:19 +0000777 /* Register OPTIONS handler */
778 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_options_handler);
779
780 /* Add OPTIONS in Allow header */
781 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_ALLOW,
782 NULL, 1, &STR_OPTIONS);
783
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000784 /* Start worker thread if needed. */
785 if (pjsua_var.ua_cfg.thread_cnt) {
786 unsigned i;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000787
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000788 if (pjsua_var.ua_cfg.thread_cnt > PJ_ARRAY_SIZE(pjsua_var.thread))
789 pjsua_var.ua_cfg.thread_cnt = PJ_ARRAY_SIZE(pjsua_var.thread);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000790
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000791 for (i=0; i<pjsua_var.ua_cfg.thread_cnt; ++i) {
792 status = pj_thread_create(pjsua_var.pool, "pjsua", &worker_thread,
793 NULL, 0, 0, &pjsua_var.thread[i]);
794 if (status != PJ_SUCCESS)
795 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000796 }
Benny Prijonof521eb02006-08-06 23:07:25 +0000797 PJ_LOG(4,(THIS_FILE, "%d SIP worker threads created",
798 pjsua_var.ua_cfg.thread_cnt));
799 } else {
800 PJ_LOG(4,(THIS_FILE, "No SIP worker threads created"));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000801 }
802
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000803 /* Done! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000804
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000805 PJ_LOG(3,(THIS_FILE, "pjsua version %s for %s initialized",
Benny Prijono106f5b72007-08-30 13:49:33 +0000806 pj_get_version(), PJ_OS_NAME));
Benny Prijonoccf95622006-02-07 18:48:01 +0000807
Benny Prijono268ca612006-02-07 12:34:11 +0000808 return PJ_SUCCESS;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000809
810on_error:
811 pjsua_destroy();
812 return status;
Benny Prijono268ca612006-02-07 12:34:11 +0000813}
814
815
Benny Prijono834aee32006-02-19 01:38:06 +0000816/* Sleep with polling */
817static void busy_sleep(unsigned msec)
818{
Benny Prijonob2c96822007-05-03 13:31:21 +0000819#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
Benny Prijono897f9f82007-05-03 19:56:21 +0000820 /* Ideally we shouldn't call pj_thread_sleep() and rather
821 * CActiveScheduler::WaitForAnyRequest() here, but that will
822 * drag in Symbian header and it doesn't look pretty.
823 */
Benny Prijonob2c96822007-05-03 13:31:21 +0000824 pj_thread_sleep(msec);
825#else
Benny Prijono834aee32006-02-19 01:38:06 +0000826 pj_time_val timeout, now;
827
828 pj_gettimeofday(&timeout);
829 timeout.msec += msec;
830 pj_time_val_normalize(&timeout);
831
832 do {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000833 while (pjsua_handle_events(10) > 0)
834 ;
Benny Prijono834aee32006-02-19 01:38:06 +0000835 pj_gettimeofday(&now);
836 } while (PJ_TIME_VAL_LT(now, timeout));
Benny Prijonob2c96822007-05-03 13:31:21 +0000837#endif
Benny Prijono834aee32006-02-19 01:38:06 +0000838}
839
Benny Prijonoebbf6892007-03-24 17:37:25 +0000840
Benny Prijonoebbf6892007-03-24 17:37:25 +0000841/*
842 * Callback function to receive notification from the resolver
843 * when the resolution process completes.
844 */
845static void stun_dns_srv_resolver_cb(void *user_data,
846 pj_status_t status,
847 const pj_dns_srv_record *rec)
848{
849 unsigned i;
850
851 PJ_UNUSED_ARG(user_data);
852
853 pjsua_var.stun_status = status;
854
855 if (status != PJ_SUCCESS) {
856 /* DNS SRV resolution failed. If stun_host is specified, resolve
857 * it with gethostbyname()
858 */
859 if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000860 pj_str_t str_host, str_port;
861 int port;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000862 pj_hostent he;
863
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000864 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
865 if (str_port.ptr != NULL) {
866 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
867 str_host.slen = (str_port.ptr - str_host.ptr);
868 str_port.ptr++;
869 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
870 str_host.slen - 1;
871 port = (int)pj_strtoul(&str_port);
872 if (port < 1 || port > 65535) {
873 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
874 pjsua_var.stun_status = PJ_EINVAL;
875 return;
876 }
877 } else {
878 str_host = pjsua_var.ua_cfg.stun_host;
879 port = 3478;
880 }
881
882 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000883
884 if (pjsua_var.stun_status == PJ_SUCCESS) {
885 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
886 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000887 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000888
889 PJ_LOG(3,(THIS_FILE,
890 "STUN server %.*s resolved, address is %s:%d",
891 (int)pjsua_var.ua_cfg.stun_host.slen,
892 pjsua_var.ua_cfg.stun_host.ptr,
893 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
894 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
895 }
896 } else {
897 char errmsg[PJ_ERR_MSG_SIZE];
898
899 pj_strerror(status, errmsg, sizeof(errmsg));
900 PJ_LOG(1,(THIS_FILE,
901 "DNS SRV resolution failed for STUN server %.*s: %s",
902 (int)pjsua_var.ua_cfg.stun_domain.slen,
903 pjsua_var.ua_cfg.stun_domain.ptr,
904 errmsg));
905 }
906 return;
907 }
908
Benny Prijonof5afa922007-06-11 16:51:18 +0000909 pj_assert(rec->count != 0 && rec->entry[0].server.addr_count != 0);
910 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL,
911 rec->entry[0].port);
912 pjsua_var.stun_srv.ipv4.sin_addr.s_addr =
913 rec->entry[0].server.addr[0].s_addr;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000914
915 PJ_LOG(3,(THIS_FILE, "_stun._udp.%.*s resolved, found %d entry(s):",
916 (int)pjsua_var.ua_cfg.stun_domain.slen,
917 pjsua_var.ua_cfg.stun_domain.ptr,
918 rec->count));
919
920 for (i=0; i<rec->count; ++i) {
921 PJ_LOG(3,(THIS_FILE,
922 " %d: prio=%d, weight=%d %s:%d",
923 i, rec->entry[i].priority, rec->entry[i].weight,
Benny Prijonof5afa922007-06-11 16:51:18 +0000924 pj_inet_ntoa(rec->entry[i].server.addr[0]),
925 (int)rec->entry[i].port));
Benny Prijonoebbf6892007-03-24 17:37:25 +0000926 }
927
928}
929
Benny Prijono268ca612006-02-07 12:34:11 +0000930/*
Benny Prijonoc97608e2007-03-23 16:34:20 +0000931 * Resolve STUN server.
932 */
933pj_status_t pjsua_resolve_stun_server(pj_bool_t wait)
934{
935 if (pjsua_var.stun_status == PJ_EUNKNOWN) {
936 /* Initialize STUN configuration */
937 pj_stun_config_init(&pjsua_var.stun_cfg, &pjsua_var.cp.factory, 0,
938 pjsip_endpt_get_ioqueue(pjsua_var.endpt),
939 pjsip_endpt_get_timer_heap(pjsua_var.endpt));
940
941 /* Start STUN server resolution */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000942
943 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000944
Benny Prijonoebbf6892007-03-24 17:37:25 +0000945 /* If stun_domain is specified, resolve STUN servers with DNS
946 * SRV resolution.
947 */
948 if (pjsua_var.ua_cfg.stun_domain.slen) {
949 pj_str_t res_type;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000950 pj_status_t status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000951
952 /* Fail if resolver is not configured */
953 if (pjsua_var.resolver == NULL) {
954 PJ_LOG(1,(THIS_FILE, "Nameserver must be configured when "
955 "stun_domain is specified"));
956 pjsua_var.stun_status = PJLIB_UTIL_EDNSNONS;
957 return PJLIB_UTIL_EDNSNONS;
958 }
959 res_type = pj_str("_stun._udp");
Benny Prijonoda9785b2007-04-02 20:43:06 +0000960 status =
Benny Prijonoebbf6892007-03-24 17:37:25 +0000961 pj_dns_srv_resolve(&pjsua_var.ua_cfg.stun_domain, &res_type,
962 3478, pjsua_var.pool, pjsua_var.resolver,
Benny Prijonof5afa922007-06-11 16:51:18 +0000963 0, NULL, &stun_dns_srv_resolver_cb, NULL);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000964 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +0000965 pjsua_perror(THIS_FILE, "Error starting DNS SRV resolution",
966 pjsua_var.stun_status);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000967 pjsua_var.stun_status = status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000968 return pjsua_var.stun_status;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000969 } else {
970 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000971 }
972 }
973 /* Otherwise if stun_host is specified, resolve STUN server with
974 * gethostbyname().
975 */
976 else if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000977 pj_str_t str_host, str_port;
978 int port;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000979 pj_hostent he;
980
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000981 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
982 if (str_port.ptr != NULL) {
983 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
984 str_host.slen = (str_port.ptr - str_host.ptr);
985 str_port.ptr++;
986 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
987 str_host.slen - 1;
988 port = (int)pj_strtoul(&str_port);
989 if (port < 1 || port > 65535) {
990 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
991 pjsua_var.stun_status = PJ_EINVAL;
992 return pjsua_var.stun_status;
993 }
994 } else {
995 str_host = pjsua_var.ua_cfg.stun_host;
996 port = 3478;
997 }
998
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000999 pjsua_var.stun_status =
1000 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, &str_host,
1001 (pj_uint16_t)port);
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001002
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001003 if (pjsua_var.stun_status != PJ_SUCCESS) {
1004 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001005
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001006 if (pjsua_var.stun_status == PJ_SUCCESS) {
1007 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
1008 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
1009 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
1010 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001011 }
Benny Prijonoebbf6892007-03-24 17:37:25 +00001012
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001013 PJ_LOG(3,(THIS_FILE,
1014 "STUN server %.*s resolved, address is %s:%d",
1015 (int)pjsua_var.ua_cfg.stun_host.slen,
1016 pjsua_var.ua_cfg.stun_host.ptr,
1017 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
1018 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
1019
Benny Prijonoc97608e2007-03-23 16:34:20 +00001020 }
Benny Prijonoebbf6892007-03-24 17:37:25 +00001021 /* Otherwise disable STUN. */
1022 else {
1023 pjsua_var.stun_status = PJ_SUCCESS;
1024 }
1025
1026
Benny Prijonoc97608e2007-03-23 16:34:20 +00001027 return pjsua_var.stun_status;
1028
1029 } else if (pjsua_var.stun_status == PJ_EPENDING) {
1030 /* STUN server resolution has been started, wait for the
1031 * result.
1032 */
Benny Prijonoebbf6892007-03-24 17:37:25 +00001033 if (wait) {
1034 while (pjsua_var.stun_status == PJ_EPENDING)
1035 pjsua_handle_events(10);
1036 }
1037
1038 return pjsua_var.stun_status;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001039
1040 } else {
1041 /* STUN server has been resolved, return the status */
1042 return pjsua_var.stun_status;
1043 }
1044}
1045
1046/*
Benny Prijono268ca612006-02-07 12:34:11 +00001047 * Destroy pjsua.
1048 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001049PJ_DEF(pj_status_t) pjsua_destroy(void)
Benny Prijono268ca612006-02-07 12:34:11 +00001050{
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001051 int i; /* Must be signed */
Benny Prijono268ca612006-02-07 12:34:11 +00001052
1053 /* Signal threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001054 pjsua_var.thread_quit_flag = 1;
Benny Prijono268ca612006-02-07 12:34:11 +00001055
1056 /* Wait worker threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001057 for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) {
1058 if (pjsua_var.thread[i]) {
1059 pj_thread_join(pjsua_var.thread[i]);
1060 pj_thread_destroy(pjsua_var.thread[i]);
1061 pjsua_var.thread[i] = NULL;
Benny Prijonoe4f2abb2006-02-10 14:04:05 +00001062 }
Benny Prijono268ca612006-02-07 12:34:11 +00001063 }
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001064
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001065 if (pjsua_var.endpt) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001066 /* Terminate all calls. */
1067 pjsua_call_hangup_all();
1068
1069 /* Terminate all presence subscriptions. */
1070 pjsua_pres_shutdown();
1071
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001072 /* Destroy pool in the buddy object */
1073 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1074 if (pjsua_var.buddy[i].pool) {
1075 pj_pool_release(pjsua_var.buddy[i].pool);
1076 pjsua_var.buddy[i].pool = NULL;
1077 }
1078 }
1079
1080 /* Destroy accounts */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001081 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1082 if (!pjsua_var.acc[i].valid)
1083 continue;
1084
1085 if (pjsua_var.acc[i].regc) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001086 pjsua_acc_set_registration(i, PJ_FALSE);
1087 }
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001088
1089 if (pjsua_var.acc[i].pool) {
1090 pj_pool_release(pjsua_var.acc[i].pool);
1091 pjsua_var.acc[i].pool = NULL;
1092 }
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001093 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001094 }
Benny Prijono834aee32006-02-19 01:38:06 +00001095
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001096 /* Destroy media */
1097 pjsua_media_subsys_destroy();
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001098
Benny Prijono268ca612006-02-07 12:34:11 +00001099 /* Destroy endpoint. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001100 if (pjsua_var.endpt) {
Benny Prijonoff3b1462008-07-14 09:32:14 +00001101 /* Wait for some time to allow unregistration and ICE/TURN
1102 * transports shutdown to complete:
1103 */
1104 PJ_LOG(4,(THIS_FILE, "Shutting down..."));
1105 busy_sleep(1000);
1106
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001107 pjsip_endpt_destroy(pjsua_var.endpt);
1108 pjsua_var.endpt = NULL;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001109 }
Benny Prijono268ca612006-02-07 12:34:11 +00001110
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001111 /* Destroy mutex */
1112 if (pjsua_var.mutex) {
1113 pj_mutex_destroy(pjsua_var.mutex);
1114 pjsua_var.mutex = NULL;
1115 }
1116
1117 /* Destroy pool and pool factory. */
1118 if (pjsua_var.pool) {
1119 pj_pool_release(pjsua_var.pool);
1120 pjsua_var.pool = NULL;
1121 pj_caching_pool_destroy(&pjsua_var.cp);
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00001122
1123 PJ_LOG(4,(THIS_FILE, "PJSUA destroyed..."));
1124
1125 /* End logging */
1126 if (pjsua_var.log_file) {
1127 pj_file_close(pjsua_var.log_file);
1128 pjsua_var.log_file = NULL;
1129 }
1130
1131 /* Shutdown PJLIB */
1132 pj_shutdown();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001133 }
Benny Prijono268ca612006-02-07 12:34:11 +00001134
Benny Prijonof762ee72006-12-01 11:14:37 +00001135 /* Clear pjsua_var */
1136 pj_bzero(&pjsua_var, sizeof(pjsua_var));
1137
Benny Prijono268ca612006-02-07 12:34:11 +00001138 /* Done. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001139 return PJ_SUCCESS;
1140}
1141
1142
1143/**
1144 * Application is recommended to call this function after all initialization
1145 * is done, so that the library can do additional checking set up
1146 * additional
1147 *
1148 * @return PJ_SUCCESS on success, or the appropriate error code.
1149 */
1150PJ_DEF(pj_status_t) pjsua_start(void)
1151{
1152 pj_status_t status;
1153
1154 status = pjsua_call_subsys_start();
1155 if (status != PJ_SUCCESS)
1156 return status;
1157
1158 status = pjsua_media_subsys_start();
1159 if (status != PJ_SUCCESS)
1160 return status;
1161
1162 status = pjsua_pres_start();
1163 if (status != PJ_SUCCESS)
1164 return status;
Benny Prijono268ca612006-02-07 12:34:11 +00001165
1166 return PJ_SUCCESS;
1167}
1168
Benny Prijono9fc735d2006-05-28 14:58:12 +00001169
1170/**
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001171 * Poll pjsua for events, and if necessary block the caller thread for
1172 * the specified maximum interval (in miliseconds).
1173 */
1174PJ_DEF(int) pjsua_handle_events(unsigned msec_timeout)
1175{
Benny Prijono897f9f82007-05-03 19:56:21 +00001176#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
1177 /* Ideally we shouldn't call pj_thread_sleep() and rather
1178 * CActiveScheduler::WaitForAnyRequest() here, but that will
1179 * drag in Symbian header and it doesn't look pretty.
1180 */
1181 pj_thread_sleep(msec_timeout);
1182 return msec_timeout;
1183#else
1184
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001185 unsigned count = 0;
1186 pj_time_val tv;
1187 pj_status_t status;
1188
1189 tv.sec = 0;
1190 tv.msec = msec_timeout;
1191 pj_time_val_normalize(&tv);
1192
1193 status = pjsip_endpt_handle_events2(pjsua_var.endpt, &tv, &count);
1194
1195 if (status != PJ_SUCCESS)
1196 return -status;
1197
1198 return count;
Benny Prijono897f9f82007-05-03 19:56:21 +00001199#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001200}
1201
1202
1203/*
1204 * Create memory pool.
1205 */
1206PJ_DEF(pj_pool_t*) pjsua_pool_create( const char *name, pj_size_t init_size,
1207 pj_size_t increment)
1208{
1209 /* Pool factory is thread safe, no need to lock */
1210 return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment,
1211 NULL);
1212}
1213
1214
1215/*
1216 * Internal function to get SIP endpoint instance of pjsua, which is
1217 * needed for example to register module, create transports, etc.
1218 * Probably is only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001219 */
1220PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void)
1221{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001222 return pjsua_var.endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001223}
1224
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001225/*
1226 * Internal function to get media endpoint instance.
1227 * Only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001228 */
1229PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void)
1230{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001231 return pjsua_var.med_endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001232}
1233
Benny Prijono97b87172006-08-24 14:25:14 +00001234/*
1235 * Internal function to get PJSUA pool factory.
1236 */
1237PJ_DEF(pj_pool_factory*) pjsua_get_pool_factory(void)
1238{
1239 return &pjsua_var.cp.factory;
1240}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001241
1242/*****************************************************************************
1243 * PJSUA SIP Transport API.
1244 */
1245
1246/*
Benny Prijono23674a32007-12-01 08:59:25 +00001247 * Tools to get address string.
1248 */
1249static const char *addr_string(const pj_sockaddr_t *addr)
1250{
1251 static char str[128];
1252 str[0] = '\0';
1253 pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family,
1254 pj_sockaddr_get_addr(addr),
1255 str, sizeof(str));
1256 return str;
1257}
1258
1259/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001260 * Create and initialize SIP socket (and possibly resolve public
1261 * address via STUN, depending on config).
1262 */
Benny Prijono23674a32007-12-01 08:59:25 +00001263static pj_status_t create_sip_udp_sock(int af,
1264 const pj_str_t *bind_param,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001265 int port,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001266 pj_sock_t *p_sock,
Benny Prijono23674a32007-12-01 08:59:25 +00001267 pj_sockaddr *p_pub_addr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001268{
Benny Prijono23674a32007-12-01 08:59:25 +00001269 char stun_ip_addr[PJ_INET6_ADDRSTRLEN];
Benny Prijonoc97608e2007-03-23 16:34:20 +00001270 pj_str_t stun_srv;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001271 pj_sock_t sock;
Benny Prijono23674a32007-12-01 08:59:25 +00001272 pj_sockaddr bind_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001273 pj_status_t status;
1274
Benny Prijonoc97608e2007-03-23 16:34:20 +00001275 /* Make sure STUN server resolution has completed */
1276 status = pjsua_resolve_stun_server(PJ_TRUE);
1277 if (status != PJ_SUCCESS) {
1278 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
1279 return status;
1280 }
1281
Benny Prijono23674a32007-12-01 08:59:25 +00001282 /* Initialize bound address */
1283 if (bind_param->slen) {
1284 status = pj_sockaddr_init(af, &bind_addr, bind_param,
1285 (pj_uint16_t)port);
1286 if (status != PJ_SUCCESS) {
1287 pjsua_perror(THIS_FILE,
1288 "Unable to resolve transport bound address",
1289 status);
1290 return status;
1291 }
1292 } else {
1293 pj_sockaddr_init(af, &bind_addr, NULL, (pj_uint16_t)port);
1294 }
1295
1296 status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001297 if (status != PJ_SUCCESS) {
1298 pjsua_perror(THIS_FILE, "socket() error", status);
Benny Prijonod8410532006-06-15 11:04:33 +00001299 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001300 }
1301
Benny Prijono5f55a1f2007-12-05 05:08:29 +00001302 status = pj_sock_bind(sock, &bind_addr, pj_sockaddr_get_len(&bind_addr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001303 if (status != PJ_SUCCESS) {
1304 pjsua_perror(THIS_FILE, "bind() error", status);
1305 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001306 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001307 }
1308
Benny Prijonoe347cb02007-02-14 14:36:13 +00001309 /* If port is zero, get the bound port */
1310 if (port == 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001311 pj_sockaddr bound_addr;
Benny Prijonoe347cb02007-02-14 14:36:13 +00001312 int namelen = sizeof(bound_addr);
1313 status = pj_sock_getsockname(sock, &bound_addr, &namelen);
1314 if (status != PJ_SUCCESS) {
1315 pjsua_perror(THIS_FILE, "getsockname() error", status);
1316 pj_sock_close(sock);
1317 return status;
1318 }
1319
Benny Prijono23674a32007-12-01 08:59:25 +00001320 port = pj_sockaddr_get_port(&bound_addr);
Benny Prijonoe347cb02007-02-14 14:36:13 +00001321 }
1322
Benny Prijonoc97608e2007-03-23 16:34:20 +00001323 if (pjsua_var.stun_srv.addr.sa_family != 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001324 pj_ansi_strcpy(stun_ip_addr,pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
1325 stun_srv = pj_str(stun_ip_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001326 } else {
Benny Prijonoc97608e2007-03-23 16:34:20 +00001327 stun_srv.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001328 }
1329
1330 /* Get the published address, either by STUN or by resolving
1331 * the name of local host.
1332 */
Benny Prijono23674a32007-12-01 08:59:25 +00001333 if (pj_sockaddr_has_addr(p_pub_addr)) {
Benny Prijono744aca02007-11-11 03:06:07 +00001334 /*
1335 * Public address is already specified, no need to resolve the
1336 * address, only set the port.
1337 */
Benny Prijono23674a32007-12-01 08:59:25 +00001338 if (pj_sockaddr_get_port(p_pub_addr) == 0)
1339 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijono744aca02007-11-11 03:06:07 +00001340
1341 } else if (stun_srv.slen) {
Benny Prijono0a5cad82006-09-26 13:21:02 +00001342 /*
1343 * STUN is specified, resolve the address with STUN.
1344 */
Benny Prijono23674a32007-12-01 08:59:25 +00001345 if (af != pj_AF_INET()) {
1346 pjsua_perror(THIS_FILE, "Cannot use STUN", PJ_EAFNOTSUP);
1347 pj_sock_close(sock);
1348 return PJ_EAFNOTSUP;
1349 }
1350
Benny Prijono14c2b862007-02-21 00:40:05 +00001351 status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001352 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
1353 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
Benny Prijono23674a32007-12-01 08:59:25 +00001354 &p_pub_addr->ipv4);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001355 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +00001356 pjsua_perror(THIS_FILE, "Error contacting STUN server", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001357 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001358 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001359 }
1360
1361 } else {
Benny Prijono23674a32007-12-01 08:59:25 +00001362 pj_bzero(p_pub_addr, sizeof(pj_sockaddr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001363
Benny Prijono42d08d22007-12-20 11:23:07 +00001364 if (pj_sockaddr_has_addr(&bind_addr)) {
1365 pj_sockaddr_copy_addr(p_pub_addr, &bind_addr);
1366 } else {
1367 status = pj_gethostip(af, p_pub_addr);
1368 if (status != PJ_SUCCESS) {
1369 pjsua_perror(THIS_FILE, "Unable to get local host IP", status);
1370 pj_sock_close(sock);
1371 return status;
1372 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001373 }
1374
Benny Prijono23674a32007-12-01 08:59:25 +00001375 p_pub_addr->addr.sa_family = (pj_uint16_t)af;
1376 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001377 }
1378
1379 *p_sock = sock;
1380
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001381 PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
Benny Prijono23674a32007-12-01 08:59:25 +00001382 addr_string(p_pub_addr),
1383 (int)pj_sockaddr_get_port(p_pub_addr)));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001384
Benny Prijonod8410532006-06-15 11:04:33 +00001385 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001386}
1387
1388
1389/*
1390 * Create SIP transport.
1391 */
1392PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
1393 const pjsua_transport_config *cfg,
1394 pjsua_transport_id *p_id)
1395{
1396 pjsip_transport *tp;
1397 unsigned id;
1398 pj_status_t status;
1399
1400 PJSUA_LOCK();
1401
1402 /* Find empty transport slot */
1403 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001404 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001405 break;
1406 }
1407
1408 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1409 status = PJ_ETOOMANY;
1410 pjsua_perror(THIS_FILE, "Error creating transport", status);
1411 goto on_return;
1412 }
1413
1414 /* Create the transport */
Benny Prijonob2477142007-12-05 04:09:59 +00001415 if (type==PJSIP_TRANSPORT_UDP || type==PJSIP_TRANSPORT_UDP6) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001416 /*
Benny Prijono23674a32007-12-01 08:59:25 +00001417 * Create UDP transport (IPv4 or IPv6).
Benny Prijonoe93e2872006-06-28 16:46:49 +00001418 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001419 pjsua_transport_config config;
Benny Prijono23674a32007-12-01 08:59:25 +00001420 char hostbuf[PJ_INET6_ADDRSTRLEN];
Benny Prijonod8410532006-06-15 11:04:33 +00001421 pj_sock_t sock = PJ_INVALID_SOCKET;
Benny Prijono23674a32007-12-01 08:59:25 +00001422 pj_sockaddr pub_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001423 pjsip_host_port addr_name;
1424
1425 /* Supply default config if it's not specified */
1426 if (cfg == NULL) {
1427 pjsua_transport_config_default(&config);
1428 cfg = &config;
1429 }
1430
Benny Prijono0a5cad82006-09-26 13:21:02 +00001431 /* Initialize the public address from the config, if any */
Benny Prijono23674a32007-12-01 08:59:25 +00001432 pj_sockaddr_init(pjsip_transport_type_get_af(type), &pub_addr,
1433 NULL, (pj_uint16_t)cfg->port);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001434 if (cfg->public_addr.slen) {
Benny Prijono23674a32007-12-01 08:59:25 +00001435 status = pj_sockaddr_set_str_addr(pjsip_transport_type_get_af(type),
1436 &pub_addr, &cfg->public_addr);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001437 if (status != PJ_SUCCESS) {
1438 pjsua_perror(THIS_FILE,
1439 "Unable to resolve transport public address",
1440 status);
1441 goto on_return;
1442 }
1443 }
1444
1445 /* Create the socket and possibly resolve the address with STUN
1446 * (only when public address is not specified).
1447 */
Benny Prijono23674a32007-12-01 08:59:25 +00001448 status = create_sip_udp_sock(pjsip_transport_type_get_af(type),
1449 &cfg->bound_addr, cfg->port,
Benny Prijono0a5cad82006-09-26 13:21:02 +00001450 &sock, &pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001451 if (status != PJ_SUCCESS)
1452 goto on_return;
1453
Benny Prijono23674a32007-12-01 08:59:25 +00001454 pj_ansi_strcpy(hostbuf, addr_string(&pub_addr));
1455 addr_name.host = pj_str(hostbuf);
1456 addr_name.port = pj_sockaddr_get_port(&pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001457
1458 /* Create UDP transport */
Benny Prijono23674a32007-12-01 08:59:25 +00001459 status = pjsip_udp_transport_attach2(pjsua_var.endpt, type, sock,
1460 &addr_name, 1, &tp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001461 if (status != PJ_SUCCESS) {
1462 pjsua_perror(THIS_FILE, "Error creating SIP UDP transport",
1463 status);
1464 pj_sock_close(sock);
1465 goto on_return;
1466 }
1467
Benny Prijonoe93e2872006-06-28 16:46:49 +00001468
1469 /* Save the transport */
1470 pjsua_var.tpdata[id].type = type;
1471 pjsua_var.tpdata[id].local_name = tp->local_name;
1472 pjsua_var.tpdata[id].data.tp = tp;
1473
Benny Prijono3569c0d2007-04-06 10:29:20 +00001474#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
1475
Benny Prijonob2477142007-12-05 04:09:59 +00001476 } else if (type == PJSIP_TRANSPORT_TCP || type == PJSIP_TRANSPORT_TCP6) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001477 /*
1478 * Create TCP transport.
1479 */
1480 pjsua_transport_config config;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001481 pjsip_host_port a_name;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001482 pjsip_tpfactory *tcp;
1483 pj_sockaddr_in local_addr;
1484
1485 /* Supply default config if it's not specified */
1486 if (cfg == NULL) {
1487 pjsua_transport_config_default(&config);
1488 cfg = &config;
1489 }
1490
1491 /* Init local address */
1492 pj_sockaddr_in_init(&local_addr, 0, 0);
1493
1494 if (cfg->port)
1495 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1496
Benny Prijono0a5cad82006-09-26 13:21:02 +00001497 if (cfg->bound_addr.slen) {
1498 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1499 if (status != PJ_SUCCESS) {
1500 pjsua_perror(THIS_FILE,
1501 "Unable to resolve transport bound address",
1502 status);
1503 goto on_return;
1504 }
1505 }
1506
1507 /* Init published name */
1508 pj_bzero(&a_name, sizeof(pjsip_host_port));
1509 if (cfg->public_addr.slen)
1510 a_name.host = cfg->public_addr;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001511
1512 /* Create the TCP transport */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001513 status = pjsip_tcp_transport_start2(pjsua_var.endpt, &local_addr,
1514 &a_name, 1, &tcp);
Benny Prijonoe93e2872006-06-28 16:46:49 +00001515
1516 if (status != PJ_SUCCESS) {
1517 pjsua_perror(THIS_FILE, "Error creating SIP TCP listener",
1518 status);
1519 goto on_return;
1520 }
1521
1522 /* Save the transport */
1523 pjsua_var.tpdata[id].type = type;
1524 pjsua_var.tpdata[id].local_name = tcp->addr_name;
1525 pjsua_var.tpdata[id].data.factory = tcp;
1526
Benny Prijono3569c0d2007-04-06 10:29:20 +00001527#endif /* PJ_HAS_TCP */
1528
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001529#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
1530 } else if (type == PJSIP_TRANSPORT_TLS) {
1531 /*
1532 * Create TLS transport.
1533 */
Benny Prijonof3bbc132006-12-25 06:43:59 +00001534 /*
1535 * Create TCP transport.
1536 */
1537 pjsua_transport_config config;
1538 pjsip_host_port a_name;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001539 pjsip_tpfactory *tls;
Benny Prijonof3bbc132006-12-25 06:43:59 +00001540 pj_sockaddr_in local_addr;
1541
1542 /* Supply default config if it's not specified */
1543 if (cfg == NULL) {
1544 pjsua_transport_config_default(&config);
1545 config.port = 5061;
1546 cfg = &config;
1547 }
1548
1549 /* Init local address */
1550 pj_sockaddr_in_init(&local_addr, 0, 0);
1551
1552 if (cfg->port)
1553 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1554
1555 if (cfg->bound_addr.slen) {
1556 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1557 if (status != PJ_SUCCESS) {
1558 pjsua_perror(THIS_FILE,
1559 "Unable to resolve transport bound address",
1560 status);
1561 goto on_return;
1562 }
1563 }
1564
1565 /* Init published name */
1566 pj_bzero(&a_name, sizeof(pjsip_host_port));
1567 if (cfg->public_addr.slen)
1568 a_name.host = cfg->public_addr;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001569
1570 status = pjsip_tls_transport_start(pjsua_var.endpt,
Benny Prijonof3bbc132006-12-25 06:43:59 +00001571 &cfg->tls_setting,
1572 &local_addr, &a_name, 1, &tls);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001573 if (status != PJ_SUCCESS) {
1574 pjsua_perror(THIS_FILE, "Error creating SIP TLS listener",
1575 status);
1576 goto on_return;
1577 }
1578
1579 /* Save the transport */
1580 pjsua_var.tpdata[id].type = type;
1581 pjsua_var.tpdata[id].local_name = tls->addr_name;
1582 pjsua_var.tpdata[id].data.factory = tls;
1583#endif
1584
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001585 } else {
1586 status = PJSIP_EUNSUPTRANSPORT;
1587 pjsua_perror(THIS_FILE, "Error creating transport", status);
1588 goto on_return;
1589 }
1590
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001591
1592 /* Return the ID */
1593 if (p_id) *p_id = id;
1594
1595 status = PJ_SUCCESS;
1596
1597on_return:
1598
1599 PJSUA_UNLOCK();
1600
Benny Prijonod8410532006-06-15 11:04:33 +00001601 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001602}
1603
1604
1605/*
1606 * Register transport that has been created by application.
1607 */
1608PJ_DEF(pj_status_t) pjsua_transport_register( pjsip_transport *tp,
1609 pjsua_transport_id *p_id)
1610{
1611 unsigned id;
1612
1613 PJSUA_LOCK();
1614
1615 /* Find empty transport slot */
1616 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001617 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001618 break;
1619 }
1620
1621 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1622 pjsua_perror(THIS_FILE, "Error creating transport", PJ_ETOOMANY);
1623 PJSUA_UNLOCK();
1624 return PJ_ETOOMANY;
1625 }
1626
1627 /* Save the transport */
Benny Prijonod17a5e92007-05-29 11:51:45 +00001628 pjsua_var.tpdata[id].type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001629 pjsua_var.tpdata[id].local_name = tp->local_name;
1630 pjsua_var.tpdata[id].data.tp = tp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001631
1632 /* Return the ID */
1633 if (p_id) *p_id = id;
1634
1635 PJSUA_UNLOCK();
1636
1637 return PJ_SUCCESS;
1638}
1639
1640
1641/*
1642 * Enumerate all transports currently created in the system.
1643 */
1644PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[],
1645 unsigned *p_count )
1646{
1647 unsigned i, count;
1648
1649 PJSUA_LOCK();
1650
1651 for (i=0, count=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata) && count<*p_count;
1652 ++i)
1653 {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001654 if (!pjsua_var.tpdata[i].data.ptr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001655 continue;
1656
1657 id[count++] = i;
1658 }
1659
1660 *p_count = count;
1661
1662 PJSUA_UNLOCK();
1663
1664 return PJ_SUCCESS;
1665}
1666
1667
1668/*
1669 * Get information about transports.
1670 */
1671PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
1672 pjsua_transport_info *info)
1673{
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001674 pjsua_transport_data *t = &pjsua_var.tpdata[id];
Benny Prijono0a5cad82006-09-26 13:21:02 +00001675 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001676
Benny Prijonoac623b32006-07-03 15:19:31 +00001677 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001678
1679 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001680 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1681 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001682
1683 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001684 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001685
1686 PJSUA_LOCK();
1687
Benny Prijonoe93e2872006-06-28 16:46:49 +00001688 if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_UDP) {
1689
1690 pjsip_transport *tp = t->data.tp;
1691
1692 if (tp == NULL) {
1693 PJSUA_UNLOCK();
1694 return PJ_EINVALIDOP;
1695 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001696
Benny Prijonoe93e2872006-06-28 16:46:49 +00001697 info->id = id;
Benny Prijonod17a5e92007-05-29 11:51:45 +00001698 info->type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001699 info->type_name = pj_str(tp->type_name);
1700 info->info = pj_str(tp->info);
1701 info->flag = tp->flag;
1702 info->addr_len = tp->addr_len;
1703 info->local_addr = tp->local_addr;
1704 info->local_name = tp->local_name;
1705 info->usage_count = pj_atomic_get(tp->ref_cnt);
1706
Benny Prijono0a5cad82006-09-26 13:21:02 +00001707 status = PJ_SUCCESS;
1708
Benny Prijonoe93e2872006-06-28 16:46:49 +00001709 } else if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_TCP) {
1710
1711 pjsip_tpfactory *factory = t->data.factory;
1712
1713 if (factory == NULL) {
1714 PJSUA_UNLOCK();
1715 return PJ_EINVALIDOP;
1716 }
1717
1718 info->id = id;
1719 info->type = t->type;
1720 info->type_name = pj_str("TCP");
1721 info->info = pj_str("TCP transport");
1722 info->flag = factory->flag;
1723 info->addr_len = sizeof(factory->local_addr);
1724 info->local_addr = factory->local_addr;
1725 info->local_name = factory->addr_name;
1726 info->usage_count = 0;
1727
Benny Prijono0a5cad82006-09-26 13:21:02 +00001728 status = PJ_SUCCESS;
1729
1730 } else {
1731 pj_assert(!"Unsupported transport");
1732 status = PJ_EINVALIDOP;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001733 }
1734
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001735
1736 PJSUA_UNLOCK();
1737
Benny Prijono0a5cad82006-09-26 13:21:02 +00001738 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001739}
1740
1741
1742/*
1743 * Disable a transport or re-enable it.
1744 */
1745PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id,
1746 pj_bool_t enabled)
1747{
1748 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001749 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1750 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001751
1752 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001753 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001754
1755
1756 /* To be done!! */
1757 PJ_TODO(pjsua_transport_set_enable);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001758 PJ_UNUSED_ARG(enabled);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001759
1760 return PJ_EINVALIDOP;
1761}
1762
1763
1764/*
1765 * Close the transport.
1766 */
1767PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
1768 pj_bool_t force )
1769{
Benny Prijono5ff61872007-02-01 03:37:11 +00001770 pj_status_t status;
1771
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001772 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001773 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1774 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001775
1776 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001777 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001778
Benny Prijono0a5cad82006-09-26 13:21:02 +00001779 /* Note: destroy() may not work if there are objects still referencing
1780 * the transport.
1781 */
1782 if (force) {
1783 switch (pjsua_var.tpdata[id].type) {
1784 case PJSIP_TRANSPORT_UDP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001785 status = pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
1786 if (status != PJ_SUCCESS)
1787 return status;
1788 status = pjsip_transport_destroy(pjsua_var.tpdata[id].data.tp);
1789 if (status != PJ_SUCCESS)
1790 return status;
1791 break;
1792
1793 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001794 case PJSIP_TRANSPORT_TCP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001795 /* This will close the TCP listener, but existing TCP/TLS
1796 * connections (if any) will still linger
1797 */
1798 status = (*pjsua_var.tpdata[id].data.factory->destroy)
1799 (pjsua_var.tpdata[id].data.factory);
1800 if (status != PJ_SUCCESS)
1801 return status;
1802
Benny Prijono0a5cad82006-09-26 13:21:02 +00001803 break;
Benny Prijono5ff61872007-02-01 03:37:11 +00001804
Benny Prijono1ebd6142006-10-19 15:48:02 +00001805 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001806 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001807 }
1808
1809 } else {
Benny Prijono5ff61872007-02-01 03:37:11 +00001810 /* If force is not specified, transports will be closed at their
1811 * convenient time. However this will leak PJSUA-API transport
1812 * descriptors as PJSUA-API wouldn't know when exactly the
1813 * transport is closed thus it can't cleanup PJSUA transport
1814 * descriptor.
1815 */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001816 switch (pjsua_var.tpdata[id].type) {
1817 case PJSIP_TRANSPORT_UDP:
1818 return pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
Benny Prijono5ff61872007-02-01 03:37:11 +00001819 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001820 case PJSIP_TRANSPORT_TCP:
1821 return (*pjsua_var.tpdata[id].data.factory->destroy)
1822 (pjsua_var.tpdata[id].data.factory);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001823 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001824 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001825 }
1826 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001827
Benny Prijono5ff61872007-02-01 03:37:11 +00001828 /* Cleanup pjsua data when force is applied */
1829 if (force) {
1830 pjsua_var.tpdata[id].type = PJSIP_TRANSPORT_UNSPECIFIED;
1831 pjsua_var.tpdata[id].data.ptr = NULL;
1832 }
1833
1834 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001835}
1836
1837
1838/*
1839 * Add additional headers etc in msg_data specified by application
1840 * when sending requests.
1841 */
1842void pjsua_process_msg_data(pjsip_tx_data *tdata,
1843 const pjsua_msg_data *msg_data)
1844{
1845 pj_bool_t allow_body;
1846 const pjsip_hdr *hdr;
1847
Benny Prijono56315612006-07-18 14:39:40 +00001848 /* Always add User-Agent */
1849 if (pjsua_var.ua_cfg.user_agent.slen &&
1850 tdata->msg->type == PJSIP_REQUEST_MSG)
1851 {
1852 const pj_str_t STR_USER_AGENT = { "User-Agent", 10 };
1853 pjsip_hdr *h;
1854 h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool,
1855 &STR_USER_AGENT,
1856 &pjsua_var.ua_cfg.user_agent);
1857 pjsip_msg_add_hdr(tdata->msg, h);
1858 }
1859
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001860 if (!msg_data)
1861 return;
1862
1863 hdr = msg_data->hdr_list.next;
1864 while (hdr && hdr != &msg_data->hdr_list) {
1865 pjsip_hdr *new_hdr;
1866
Benny Prijonoa1e69682007-05-11 15:14:34 +00001867 new_hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001868 pjsip_msg_add_hdr(tdata->msg, new_hdr);
1869
1870 hdr = hdr->next;
1871 }
1872
1873 allow_body = (tdata->msg->body == NULL);
1874
1875 if (allow_body && msg_data->content_type.slen && msg_data->msg_body.slen) {
1876 pjsip_media_type ctype;
1877 pjsip_msg_body *body;
1878
1879 pjsua_parse_media_type(tdata->pool, &msg_data->content_type, &ctype);
1880 body = pjsip_msg_body_create(tdata->pool, &ctype.type, &ctype.subtype,
1881 &msg_data->msg_body);
1882 tdata->msg->body = body;
1883 }
1884}
1885
1886
1887/*
1888 * Add route_set to outgoing requests
1889 */
1890void pjsua_set_msg_route_set( pjsip_tx_data *tdata,
1891 const pjsip_route_hdr *route_set )
1892{
1893 const pjsip_route_hdr *r;
1894
1895 r = route_set->next;
1896 while (r != route_set) {
1897 pjsip_route_hdr *new_r;
1898
Benny Prijonoa1e69682007-05-11 15:14:34 +00001899 new_r = (pjsip_route_hdr*) pjsip_hdr_clone(tdata->pool, r);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001900 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)new_r);
1901
1902 r = r->next;
1903 }
1904}
1905
1906
1907/*
1908 * Simple version of MIME type parsing (it doesn't support parameters)
1909 */
1910void pjsua_parse_media_type( pj_pool_t *pool,
1911 const pj_str_t *mime,
1912 pjsip_media_type *media_type)
1913{
1914 pj_str_t tmp;
1915 char *pos;
1916
Benny Prijonoac623b32006-07-03 15:19:31 +00001917 pj_bzero(media_type, sizeof(*media_type));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001918
1919 pj_strdup_with_null(pool, &tmp, mime);
1920
1921 pos = pj_strchr(&tmp, '/');
1922 if (pos) {
1923 media_type->type.ptr = tmp.ptr;
1924 media_type->type.slen = (pos-tmp.ptr);
1925 media_type->subtype.ptr = pos+1;
1926 media_type->subtype.slen = tmp.ptr+tmp.slen-pos-1;
1927 } else {
1928 media_type->type = tmp;
1929 }
1930}
1931
1932
1933/*
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001934 * Internal function to init transport selector from transport id.
1935 */
1936void pjsua_init_tpselector(pjsua_transport_id tp_id,
1937 pjsip_tpselector *sel)
1938{
1939 pjsua_transport_data *tpdata;
1940 unsigned flag;
1941
1942 pj_bzero(sel, sizeof(*sel));
1943 if (tp_id == PJSUA_INVALID_ID)
1944 return;
1945
Benny Prijonoa1e69682007-05-11 15:14:34 +00001946 pj_assert(tp_id >= 0 && tp_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata));
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001947 tpdata = &pjsua_var.tpdata[tp_id];
1948
1949 flag = pjsip_transport_get_flag_from_type(tpdata->type);
1950
1951 if (flag & PJSIP_TRANSPORT_DATAGRAM) {
1952 sel->type = PJSIP_TPSELECTOR_TRANSPORT;
1953 sel->u.transport = tpdata->data.tp;
1954 } else {
1955 sel->type = PJSIP_TPSELECTOR_LISTENER;
1956 sel->u.listener = tpdata->data.factory;
1957 }
1958}
1959
1960
Benny Prijono6ba8c542007-10-16 01:34:14 +00001961/* Callback upon NAT detection completion */
1962static void nat_detect_cb(void *user_data,
1963 const pj_stun_nat_detect_result *res)
1964{
1965 PJ_UNUSED_ARG(user_data);
1966
1967 pjsua_var.nat_in_progress = PJ_FALSE;
1968 pjsua_var.nat_status = res->status;
1969 pjsua_var.nat_type = res->nat_type;
1970
1971 if (pjsua_var.ua_cfg.cb.on_nat_detect) {
1972 (*pjsua_var.ua_cfg.cb.on_nat_detect)(res);
1973 }
1974}
1975
1976
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001977/*
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001978 * Detect NAT type.
1979 */
Benny Prijono6ba8c542007-10-16 01:34:14 +00001980PJ_DEF(pj_status_t) pjsua_detect_nat_type()
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001981{
1982 pj_status_t status;
1983
Benny Prijono6ba8c542007-10-16 01:34:14 +00001984 if (pjsua_var.nat_in_progress)
1985 return PJ_SUCCESS;
1986
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001987 /* Make sure STUN server resolution has completed */
1988 status = pjsua_resolve_stun_server(PJ_TRUE);
1989 if (status != PJ_SUCCESS) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00001990 pjsua_var.nat_status = status;
1991 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001992 return status;
1993 }
1994
1995 /* Make sure we have STUN */
1996 if (pjsua_var.stun_srv.ipv4.sin_family == 0) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00001997 pjsua_var.nat_status = PJNATH_ESTUNINSERVER;
1998 return PJNATH_ESTUNINSERVER;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001999 }
2000
Benny Prijono6ba8c542007-10-16 01:34:14 +00002001 status = pj_stun_detect_nat_type(&pjsua_var.stun_srv.ipv4,
2002 &pjsua_var.stun_cfg,
2003 NULL, &nat_detect_cb);
2004
2005 if (status != PJ_SUCCESS) {
2006 pjsua_var.nat_status = status;
2007 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
2008 return status;
2009 }
2010
2011 pjsua_var.nat_in_progress = PJ_TRUE;
2012
2013 return PJ_SUCCESS;
2014}
2015
2016
2017/*
2018 * Get NAT type.
2019 */
2020PJ_DEF(pj_status_t) pjsua_get_nat_type(pj_stun_nat_type *type)
2021{
2022 *type = pjsua_var.nat_type;
2023 return pjsua_var.nat_status;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002024}
2025
2026
2027/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002028 * Verify that valid SIP url is given.
2029 */
2030PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url)
2031{
2032 pjsip_uri *p;
2033 pj_pool_t *pool;
2034 char *url;
2035 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
2036
2037 if (!len) return -1;
2038
2039 pool = pj_pool_create(&pjsua_var.cp.factory, "check%p", 1024, 0, NULL);
2040 if (!pool) return -1;
2041
Benny Prijonoa1e69682007-05-11 15:14:34 +00002042 url = (char*) pj_pool_alloc(pool, len+1);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002043 pj_ansi_strcpy(url, c_url);
2044
2045 p = pjsip_parse_uri(pool, url, len, 0);
Benny Prijonocf0b4b22007-10-06 17:31:09 +00002046 if (!p || (pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0 &&
2047 pj_stricmp2(pjsip_uri_get_scheme(p), "sips") != 0))
2048 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002049 p = NULL;
Benny Prijonocf0b4b22007-10-06 17:31:09 +00002050 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002051
2052 pj_pool_release(pool);
2053 return p ? 0 : -1;
2054}
Benny Prijonoda9785b2007-04-02 20:43:06 +00002055
2056
2057/*
2058 * This is a utility function to dump the stack states to log, using
2059 * verbosity level 3.
2060 */
2061PJ_DEF(void) pjsua_dump(pj_bool_t detail)
2062{
2063 unsigned old_decor;
2064 unsigned i;
2065 char buf[1024];
2066
2067 PJ_LOG(3,(THIS_FILE, "Start dumping application states:"));
2068
2069 old_decor = pj_log_get_decor();
2070 pj_log_set_decor(old_decor & (PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
2071
2072 if (detail)
2073 pj_dump_config();
2074
2075 pjsip_endpt_dump(pjsua_get_pjsip_endpt(), detail);
2076
2077 pjmedia_endpt_dump(pjsua_get_pjmedia_endpt());
2078
2079 PJ_LOG(3,(THIS_FILE, "Dumping media transports:"));
2080 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2081 pjsua_call *call = &pjsua_var.calls[i];
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002082 pjmedia_transport_info tpinfo;
Benny Prijono5186eae2007-12-03 14:38:25 +00002083 char addr_buf[80];
Benny Prijonoda9785b2007-04-02 20:43:06 +00002084
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002085 /* MSVC complains about tpinfo not being initialized */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002086 //pj_bzero(&tpinfo, sizeof(tpinfo));
Benny Prijono4f093d22007-04-09 08:54:32 +00002087
Benny Prijono734fc2d2008-03-17 16:05:35 +00002088 pjmedia_transport_info_init(&tpinfo);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002089 pjmedia_transport_get_info(call->med_tp, &tpinfo);
Benny Prijonoda9785b2007-04-02 20:43:06 +00002090
Benny Prijono5186eae2007-12-03 14:38:25 +00002091 PJ_LOG(3,(THIS_FILE, " %s: %s",
Benny Prijonoda9785b2007-04-02 20:43:06 +00002092 (pjsua_var.media_cfg.enable_ice ? "ICE" : "UDP"),
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002093 pj_sockaddr_print(&tpinfo.sock_info.rtp_addr_name, addr_buf,
Benny Prijono5186eae2007-12-03 14:38:25 +00002094 sizeof(addr_buf), 3)));
Benny Prijonoda9785b2007-04-02 20:43:06 +00002095 }
2096
2097 pjsip_tsx_layer_dump(detail);
2098 pjsip_ua_dump(detail);
2099
2100
2101 /* Dump all invite sessions: */
2102 PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
2103
2104 if (pjsua_call_get_count() == 0) {
2105
2106 PJ_LOG(3,(THIS_FILE, " - no sessions -"));
2107
2108 } else {
2109 unsigned i;
2110
2111 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2112 if (pjsua_call_is_active(i)) {
2113 pjsua_call_dump(i, detail, buf, sizeof(buf), " ");
2114 PJ_LOG(3,(THIS_FILE, "%s", buf));
2115 }
2116 }
2117 }
2118
2119 /* Dump presence status */
2120 pjsua_pres_dump(detail);
2121
2122 pj_log_set_decor(old_decor);
2123 PJ_LOG(3,(THIS_FILE, "Dump complete"));
2124}
2125