blob: 404715837c6a75fc5685481ffd42b8791760b0d7 [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 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);
116 pj_strdup_with_null(pool, &dst->stun_relay_host, &src->stun_relay_host);
117}
118
119PJ_DEF(void) pjsua_msg_data_init(pjsua_msg_data *msg_data)
120{
121 pj_bzero(msg_data, sizeof(*msg_data));
122 pj_list_init(&msg_data->hdr_list);
123}
124
125PJ_DEF(void) pjsua_transport_config_default(pjsua_transport_config *cfg)
126{
127 pj_bzero(cfg, sizeof(*cfg));
128 pjsip_tls_setting_default(&cfg->tls_setting);
129}
130
131PJ_DEF(void) pjsua_transport_config_dup(pj_pool_t *pool,
132 pjsua_transport_config *dst,
133 const pjsua_transport_config *src)
134{
135 PJ_UNUSED_ARG(pool);
136 pj_memcpy(dst, src, sizeof(*src));
137}
138
139PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg)
140{
141 pj_bzero(cfg, sizeof(*cfg));
142
143 cfg->reg_timeout = PJSUA_REG_INTERVAL;
144 cfg->transport_id = PJSUA_INVALID_ID;
Benny Prijono15b02302007-09-27 14:07:07 +0000145 cfg->auto_update_nat = PJ_TRUE;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000146 cfg->require_100rel = pjsua_var.ua_cfg.require_100rel;
Benny Prijonobddef2c2007-10-31 13:28:08 +0000147 cfg->ka_interval = 15;
148 cfg->ka_data = pj_str("\r\n");
Benny Prijonod8179652008-01-23 20:39:07 +0000149#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
150 cfg->use_srtp = pjsua_var.ua_cfg.use_srtp;
151 cfg->srtp_secure_signaling = pjsua_var.ua_cfg.srtp_secure_signaling;
152#endif
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000153}
154
155PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg)
156{
157 pj_bzero(cfg, sizeof(*cfg));
158}
159
160PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg)
161{
162 pj_bzero(cfg, sizeof(*cfg));
163
164 cfg->clock_rate = PJSUA_DEFAULT_CLOCK_RATE;
Benny Prijono50f19b32008-03-11 13:15:43 +0000165 cfg->snd_clock_rate = 0;
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;
174}
175
Benny Prijonodc39fe82006-05-26 12:17:46 +0000176
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000177/*****************************************************************************
178 * This is a very simple PJSIP module, whose sole purpose is to display
179 * incoming and outgoing messages to log. This module will have priority
180 * higher than transport layer, which means:
181 *
182 * - incoming messages will come to this module first before reaching
183 * transaction layer.
184 *
185 * - outgoing messages will come to this module last, after the message
186 * has been 'printed' to contiguous buffer by transport layer and
187 * appropriate transport instance has been decided for this message.
188 *
189 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000190
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000191/* Notification on incoming messages */
192static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
193{
Benny Prijonob4a17c92006-07-10 14:40:21 +0000194 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
195 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000196 "--end msg--",
197 rdata->msg_info.len,
198 pjsip_rx_data_get_info(rdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000199 rdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000200 rdata->pkt_info.src_name,
201 rdata->pkt_info.src_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000202 (int)rdata->msg_info.len,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000203 rdata->msg_info.msg_buf));
204
205 /* Always return false, otherwise messages will not get processed! */
206 return PJ_FALSE;
207}
Benny Prijonodc39fe82006-05-26 12:17:46 +0000208
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000209/* Notification on outgoing messages */
210static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
211{
212
213 /* Important note:
214 * tp_info field is only valid after outgoing messages has passed
215 * transport layer. So don't try to access tp_info when the module
216 * has lower priority than transport layer.
217 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000218
Benny Prijonob4a17c92006-07-10 14:40:21 +0000219 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
220 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000221 "--end msg--",
222 (tdata->buf.cur - tdata->buf.start),
223 pjsip_tx_data_get_info(tdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000224 tdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000225 tdata->tp_info.dst_name,
226 tdata->tp_info.dst_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000227 (int)(tdata->buf.cur - tdata->buf.start),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000228 tdata->buf.start));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000229
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000230 /* Always return success, otherwise message will not get sent! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000231 return PJ_SUCCESS;
232}
233
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000234/* The module instance. */
235static pjsip_module pjsua_msg_logger =
236{
237 NULL, NULL, /* prev, next. */
238 { "mod-pjsua-log", 13 }, /* Name. */
239 -1, /* Id */
240 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
241 NULL, /* load() */
242 NULL, /* start() */
243 NULL, /* stop() */
244 NULL, /* unload() */
245 &logging_on_rx_msg, /* on_rx_request() */
246 &logging_on_rx_msg, /* on_rx_response() */
247 &logging_on_tx_msg, /* on_tx_request. */
248 &logging_on_tx_msg, /* on_tx_response() */
249 NULL, /* on_tsx_state() */
250
251};
252
253
254/*****************************************************************************
Benny Prijono56315612006-07-18 14:39:40 +0000255 * Another simple module to handle incoming OPTIONS request
256 */
257
258/* Notification on incoming request */
259static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
260{
261 pjsip_tx_data *tdata;
262 pjsip_response_addr res_addr;
Benny Prijono617c5bc2007-04-02 19:51:21 +0000263 pjmedia_sock_info skinfo;
Benny Prijono56315612006-07-18 14:39:40 +0000264 pjmedia_sdp_session *sdp;
265 const pjsip_hdr *cap_hdr;
266 pj_status_t status;
267
268 /* Only want to handle OPTIONS requests */
269 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000270 pjsip_get_options_method()) != 0)
Benny Prijono56315612006-07-18 14:39:40 +0000271 {
272 return PJ_FALSE;
273 }
274
275 /* Create basic response. */
276 status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL,
277 &tdata);
278 if (status != PJ_SUCCESS) {
279 pjsua_perror(THIS_FILE, "Unable to create OPTIONS response", status);
280 return PJ_TRUE;
281 }
282
283 /* Add Allow header */
284 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ALLOW, NULL);
285 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000286 pjsip_msg_add_hdr(tdata->msg,
287 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000288 }
289
290 /* Add Accept header */
291 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL);
292 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000293 pjsip_msg_add_hdr(tdata->msg,
294 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000295 }
296
297 /* Add Supported header */
298 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_SUPPORTED, NULL);
299 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000300 pjsip_msg_add_hdr(tdata->msg,
301 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000302 }
303
304 /* Add Allow-Events header from the evsub module */
305 cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL);
306 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000307 pjsip_msg_add_hdr(tdata->msg,
308 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000309 }
310
311 /* Add User-Agent header */
312 if (pjsua_var.ua_cfg.user_agent.slen) {
313 const pj_str_t USER_AGENT = { "User-Agent", 10};
314 pjsip_hdr *h;
315
316 h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
317 &USER_AGENT,
318 &pjsua_var.ua_cfg.user_agent);
319 pjsip_msg_add_hdr(tdata->msg, h);
320 }
321
Benny Prijono617c5bc2007-04-02 19:51:21 +0000322 /* Get media socket info */
323 pjmedia_transport_get_info(pjsua_var.calls[0].med_tp, &skinfo);
324
Benny Prijono56315612006-07-18 14:39:40 +0000325 /* Add SDP body, using call0's RTP address */
326 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool, 1,
Benny Prijono617c5bc2007-04-02 19:51:21 +0000327 &skinfo, &sdp);
Benny Prijono56315612006-07-18 14:39:40 +0000328 if (status == PJ_SUCCESS) {
329 pjsip_create_sdp_body(tdata->pool, sdp, &tdata->msg->body);
330 }
331
332 /* Send response statelessly */
333 pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
334 status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, tdata, NULL, NULL);
335 if (status != PJ_SUCCESS)
336 pjsip_tx_data_dec_ref(tdata);
337
338 return PJ_TRUE;
339}
340
341
342/* The module instance. */
343static pjsip_module pjsua_options_handler =
344{
345 NULL, NULL, /* prev, next. */
346 { "mod-pjsua-options", 17 }, /* Name. */
347 -1, /* Id */
348 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
349 NULL, /* load() */
350 NULL, /* start() */
351 NULL, /* stop() */
352 NULL, /* unload() */
353 &options_on_rx_request, /* on_rx_request() */
354 NULL, /* on_rx_response() */
355 NULL, /* on_tx_request. */
356 NULL, /* on_tx_response() */
357 NULL, /* on_tsx_state() */
358
359};
360
361
362/*****************************************************************************
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000363 * These two functions are the main callbacks registered to PJSIP stack
364 * to receive SIP request and response messages that are outside any
365 * dialogs and any transactions.
366 */
Benny Prijono268ca612006-02-07 12:34:11 +0000367
368/*
369 * Handler for receiving incoming requests.
370 *
371 * This handler serves multiple purposes:
372 * - it receives requests outside dialogs.
373 * - it receives requests inside dialogs, when the requests are
374 * unhandled by other dialog usages. Example of these
375 * requests are: MESSAGE.
376 */
377static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
378{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000379 pj_bool_t processed = PJ_FALSE;
380
381 PJSUA_LOCK();
Benny Prijono38998232006-02-08 22:44:25 +0000382
Benny Prijono84126ab2006-02-09 09:30:09 +0000383 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
Benny Prijono38998232006-02-08 22:44:25 +0000384
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000385 processed = pjsua_call_on_incoming(rdata);
Benny Prijono38998232006-02-08 22:44:25 +0000386 }
387
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000388 PJSUA_UNLOCK();
389
390 return processed;
Benny Prijono268ca612006-02-07 12:34:11 +0000391}
392
393
394/*
395 * Handler for receiving incoming responses.
396 *
397 * This handler serves multiple purposes:
398 * - it receives strayed responses (i.e. outside any dialog and
399 * outside any transactions).
400 * - it receives responses coming to a transaction, when pjsua
401 * module is set as transaction user for the transaction.
402 * - it receives responses inside a dialog, when these responses
403 * are unhandled by other dialog usages.
404 */
405static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
406{
407 PJ_UNUSED_ARG(rdata);
Benny Prijono268ca612006-02-07 12:34:11 +0000408 return PJ_FALSE;
409}
410
411
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000412/*****************************************************************************
413 * Logging.
414 */
415
416/* Log callback */
417static void log_writer(int level, const char *buffer, int len)
Benny Prijono268ca612006-02-07 12:34:11 +0000418{
Benny Prijono572d4852006-11-23 21:50:02 +0000419 /* Write to file, stdout or application callback. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000420
421 if (pjsua_var.log_file) {
422 pj_ssize_t size = len;
423 pj_file_write(pjsua_var.log_file, buffer, &size);
Benny Prijono980ca0a2007-04-03 17:55:08 +0000424 /* This will slow things down considerably! Don't do it!
425 pj_file_flush(pjsua_var.log_file);
426 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000427 }
428
Benny Prijono572d4852006-11-23 21:50:02 +0000429 if (level <= (int)pjsua_var.log_cfg.console_level) {
430 if (pjsua_var.log_cfg.cb)
431 (*pjsua_var.log_cfg.cb)(level, buffer, len);
432 else
433 pj_log_write(level, buffer, len);
434 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000435}
436
437
438/*
439 * Application can call this function at any time (after pjsua_create(), of
440 * course) to change logging settings.
441 */
442PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg)
443{
444 pj_status_t status;
445
446 /* Save config. */
447 pjsua_logging_config_dup(pjsua_var.pool, &pjsua_var.log_cfg, cfg);
448
449 /* Redirect log function to ours */
450 pj_log_set_log_func( &log_writer );
451
Benny Prijono9cb09a22007-08-30 09:35:10 +0000452 /* Set decor */
453 pj_log_set_decor(pjsua_var.log_cfg.decor);
454
Benny Prijono4190cf92008-01-18 13:25:05 +0000455 /* Set log level */
456 pj_log_set_level(pjsua_var.log_cfg.level);
457
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000458 /* Close existing file, if any */
459 if (pjsua_var.log_file) {
460 pj_file_close(pjsua_var.log_file);
461 pjsua_var.log_file = NULL;
462 }
463
464 /* If output log file is desired, create the file: */
465 if (pjsua_var.log_cfg.log_filename.slen) {
466
467 status = pj_file_open(pjsua_var.pool,
468 pjsua_var.log_cfg.log_filename.ptr,
469 PJ_O_WRONLY,
470 &pjsua_var.log_file);
471
472 if (status != PJ_SUCCESS) {
473 pjsua_perror(THIS_FILE, "Error creating log file", status);
474 return status;
475 }
476 }
477
478 /* Unregister msg logging if it's previously registered */
479 if (pjsua_msg_logger.id >= 0) {
480 pjsip_endpt_unregister_module(pjsua_var.endpt, &pjsua_msg_logger);
481 pjsua_msg_logger.id = -1;
482 }
483
484 /* Enable SIP message logging */
485 if (pjsua_var.log_cfg.msg_logging)
486 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_msg_logger);
487
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000488 return PJ_SUCCESS;
489}
490
491
492/*****************************************************************************
493 * PJSUA Base API.
494 */
495
496/* Worker thread function. */
497static int worker_thread(void *arg)
498{
499 enum { TIMEOUT = 10 };
Benny Prijonof8baa872006-02-27 23:54:23 +0000500
Benny Prijono268ca612006-02-07 12:34:11 +0000501 PJ_UNUSED_ARG(arg);
502
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000503 while (!pjsua_var.thread_quit_flag) {
504 int count;
505
506 count = pjsua_handle_events(TIMEOUT);
507 if (count < 0)
508 pj_thread_sleep(TIMEOUT);
509 }
Benny Prijono268ca612006-02-07 12:34:11 +0000510
511 return 0;
512}
513
Benny Prijonodc39fe82006-05-26 12:17:46 +0000514
Benny Prijono8389c312008-02-21 21:36:34 +0000515/* Init random seed */
516static void init_random_seed(void)
517{
518 pj_sockaddr addr;
519 const pj_str_t *hostname;
520 pj_uint32_t pid;
521 pj_time_val t;
522 unsigned seed=0;
523
524 /* Add hostname */
525 hostname = pj_gethostname();
526 seed = pj_hash_calc(seed, hostname->ptr, (int)hostname->slen);
527
528 /* Add primary IP address */
529 if (pj_gethostip(pj_AF_INET(), &addr)==PJ_SUCCESS)
530 seed = pj_hash_calc(seed, &addr.ipv4.sin_addr, 4);
531
532 /* Get timeofday */
533 pj_gettimeofday(&t);
534 seed = pj_hash_calc(seed, &t, sizeof(t));
535
536 /* Add PID */
537 pid = pj_getpid();
538 seed = pj_hash_calc(seed, &pid, sizeof(pid));
539
540 /* Init random seed */
541 pj_srand(seed);
542}
543
Benny Prijono268ca612006-02-07 12:34:11 +0000544/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000545 * Instantiate pjsua application.
Benny Prijonodc39fe82006-05-26 12:17:46 +0000546 */
547PJ_DEF(pj_status_t) pjsua_create(void)
548{
549 pj_status_t status;
Benny Prijono268ca612006-02-07 12:34:11 +0000550
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000551 /* Init pjsua data */
552 init_data();
Benny Prijono268ca612006-02-07 12:34:11 +0000553
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000554 /* Set default logging settings */
555 pjsua_logging_config_default(&pjsua_var.log_cfg);
556
557 /* Init PJLIB: */
Benny Prijono268ca612006-02-07 12:34:11 +0000558 status = pj_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000559 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
560
Benny Prijono8389c312008-02-21 21:36:34 +0000561 /* Init random seed */
562 init_random_seed();
Benny Prijono268ca612006-02-07 12:34:11 +0000563
Benny Prijonofccab712006-02-22 22:23:22 +0000564 /* Init PJLIB-UTIL: */
Benny Prijonofccab712006-02-22 22:23:22 +0000565 status = pjlib_util_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000566 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonofccab712006-02-22 22:23:22 +0000567
Benny Prijonoec921342007-03-24 13:00:30 +0000568 /* Init PJNATH */
569 status = pjnath_init();
570 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono268ca612006-02-07 12:34:11 +0000571
Benny Prijono094d3ad2006-11-21 08:41:00 +0000572 /* Set default sound device ID */
573 pjsua_var.cap_dev = pjsua_var.play_dev = -1;
574
Benny Prijono268ca612006-02-07 12:34:11 +0000575 /* Init caching pool. */
Benny Prijono106f5b72007-08-30 13:49:33 +0000576 pj_caching_pool_init(&pjsua_var.cp, NULL, 0);
Benny Prijono268ca612006-02-07 12:34:11 +0000577
578 /* Create memory pool for application. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000579 pjsua_var.pool = pjsua_pool_create("pjsua", 4000, 4000);
580
581 PJ_ASSERT_RETURN(pjsua_var.pool, PJ_ENOMEM);
Benny Prijono268ca612006-02-07 12:34:11 +0000582
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000583 /* Create mutex */
584 status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua",
585 &pjsua_var.mutex);
Benny Prijonoccf95622006-02-07 18:48:01 +0000586 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000587 pjsua_perror(THIS_FILE, "Unable to create mutex", status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000588 return status;
589 }
590
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000591 /* Must create SIP endpoint to initialize SIP parser. The parser
592 * is needed for example when application needs to call pjsua_verify_url().
Benny Prijonob8c25182006-04-29 08:31:09 +0000593 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000594 status = pjsip_endpt_create(&pjsua_var.cp.factory,
595 pj_gethostname()->ptr,
596 &pjsua_var.endpt);
597 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono39879152006-02-23 02:09:10 +0000598
599
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000600 return PJ_SUCCESS;
601}
602
603
604/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000605 * Initialize pjsua with the specified settings. All the settings are
606 * optional, and the default values will be used when the config is not
607 * specified.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000608 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000609PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
610 const pjsua_logging_config *log_cfg,
611 const pjsua_media_config *media_cfg)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000612{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000613 pjsua_config default_cfg;
614 pjsua_media_config default_media_cfg;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000615 const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
Benny Prijonodc39fe82006-05-26 12:17:46 +0000616 pj_status_t status;
617
618
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000619 /* Create default configurations when the config is not supplied */
620
621 if (ua_cfg == NULL) {
622 pjsua_config_default(&default_cfg);
623 ua_cfg = &default_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000624 }
625
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000626 if (media_cfg == NULL) {
627 pjsua_media_config_default(&default_media_cfg);
628 media_cfg = &default_media_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000629 }
630
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000631 /* Initialize logging first so that info/errors can be captured */
632 if (log_cfg) {
633 status = pjsua_reconfigure_logging(log_cfg);
634 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000635 }
636
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000637 /* If nameserver is configured, create DNS resolver instance and
638 * set it to be used by SIP resolver.
639 */
640 if (ua_cfg->nameserver_count) {
641#if PJSIP_HAS_RESOLVER
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000642 unsigned i;
643
644 /* Create DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000645 status = pjsip_endpt_create_resolver(pjsua_var.endpt,
646 &pjsua_var.resolver);
Benny Prijono318f3842007-05-15 20:27:08 +0000647 if (status != PJ_SUCCESS) {
648 pjsua_perror(THIS_FILE, "Error creating resolver", status);
649 return status;
650 }
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000651
652 /* Configure nameserver for the DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000653 status = pj_dns_resolver_set_ns(pjsua_var.resolver,
654 ua_cfg->nameserver_count,
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000655 ua_cfg->nameserver, NULL);
656 if (status != PJ_SUCCESS) {
657 pjsua_perror(THIS_FILE, "Error setting nameserver", status);
658 return status;
659 }
660
661 /* Set this DNS resolver to be used by the SIP resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000662 status = pjsip_endpt_set_resolver(pjsua_var.endpt, pjsua_var.resolver);
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000663 if (status != PJ_SUCCESS) {
664 pjsua_perror(THIS_FILE, "Error setting DNS resolver", status);
665 return status;
666 }
667
668 /* Print nameservers */
669 for (i=0; i<ua_cfg->nameserver_count; ++i) {
670 PJ_LOG(4,(THIS_FILE, "Nameserver %.*s added",
671 (int)ua_cfg->nameserver[i].slen,
672 ua_cfg->nameserver[i].ptr));
673 }
674#else
675 PJ_LOG(2,(THIS_FILE,
676 "DNS resolver is disabled (PJSIP_HAS_RESOLVER==0)"));
677#endif
678 }
679
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000680 /* Init SIP UA: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000681
682 /* Initialize transaction layer: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000683 status = pjsip_tsx_layer_init_module(pjsua_var.endpt);
684 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000685
Benny Prijonodc39fe82006-05-26 12:17:46 +0000686
687 /* Initialize UA layer module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000688 status = pjsip_ua_init_module( pjsua_var.endpt, NULL );
689 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000690
Benny Prijonodc39fe82006-05-26 12:17:46 +0000691
Benny Prijono053f5222006-11-11 16:16:04 +0000692 /* Initialize Replaces support. */
693 status = pjsip_replaces_init_module( pjsua_var.endpt );
694 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
695
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000696 /* Initialize 100rel support */
697 status = pjsip_100rel_init_module(pjsua_var.endpt);
698 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono053f5222006-11-11 16:16:04 +0000699
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000700 /* Initialize and register PJSUA application module. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000701 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000702 const pjsip_module mod_initializer =
Benny Prijonodc39fe82006-05-26 12:17:46 +0000703 {
704 NULL, NULL, /* prev, next. */
705 { "mod-pjsua", 9 }, /* Name. */
706 -1, /* Id */
707 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
708 NULL, /* load() */
709 NULL, /* start() */
710 NULL, /* stop() */
711 NULL, /* unload() */
712 &mod_pjsua_on_rx_request, /* on_rx_request() */
713 &mod_pjsua_on_rx_response, /* on_rx_response() */
714 NULL, /* on_tx_request. */
715 NULL, /* on_tx_response() */
716 NULL, /* on_tsx_state() */
717 };
718
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000719 pjsua_var.mod = mod_initializer;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000720
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000721 status = pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_var.mod);
722 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000723 }
724
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000725
Benny Prijonodc39fe82006-05-26 12:17:46 +0000726
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000727 /* Initialize PJSUA call subsystem: */
728 status = pjsua_call_subsys_init(ua_cfg);
729 if (status != PJ_SUCCESS)
Benny Prijonodc39fe82006-05-26 12:17:46 +0000730 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000731
732
Benny Prijonoc97608e2007-03-23 16:34:20 +0000733 /* Start resolving STUN server */
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000734
Benny Prijonoc97608e2007-03-23 16:34:20 +0000735 status = pjsua_resolve_stun_server(PJ_FALSE);
736 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
737 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
738 return status;
739 }
740
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000741 /* Initialize PJSUA media subsystem */
742 status = pjsua_media_subsys_init(media_cfg);
743 if (status != PJ_SUCCESS)
744 goto on_error;
745
Benny Prijonodc39fe82006-05-26 12:17:46 +0000746
747 /* Init core SIMPLE module : */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000748 status = pjsip_evsub_init_module(pjsua_var.endpt);
749 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000750
Benny Prijonodc39fe82006-05-26 12:17:46 +0000751
752 /* Init presence module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000753 status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance());
754 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000755
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000756 /* Init PUBLISH module */
757 pjsip_publishc_init_module(pjsua_var.endpt);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000758
759 /* Init xfer/REFER module */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000760 status = pjsip_xfer_init_module( pjsua_var.endpt );
761 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000762
763 /* Init pjsua presence handler: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000764 status = pjsua_pres_init();
765 if (status != PJ_SUCCESS)
766 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000767
768 /* Init out-of-dialog MESSAGE request handler. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000769 status = pjsua_im_init();
770 if (status != PJ_SUCCESS)
771 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000772
Benny Prijonoc8141a82006-08-20 09:12:19 +0000773 /* Register OPTIONS handler */
774 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_options_handler);
775
776 /* Add OPTIONS in Allow header */
777 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_ALLOW,
778 NULL, 1, &STR_OPTIONS);
779
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000780 /* Start worker thread if needed. */
781 if (pjsua_var.ua_cfg.thread_cnt) {
782 unsigned i;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000783
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000784 if (pjsua_var.ua_cfg.thread_cnt > PJ_ARRAY_SIZE(pjsua_var.thread))
785 pjsua_var.ua_cfg.thread_cnt = PJ_ARRAY_SIZE(pjsua_var.thread);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000786
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000787 for (i=0; i<pjsua_var.ua_cfg.thread_cnt; ++i) {
788 status = pj_thread_create(pjsua_var.pool, "pjsua", &worker_thread,
789 NULL, 0, 0, &pjsua_var.thread[i]);
790 if (status != PJ_SUCCESS)
791 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000792 }
Benny Prijonof521eb02006-08-06 23:07:25 +0000793 PJ_LOG(4,(THIS_FILE, "%d SIP worker threads created",
794 pjsua_var.ua_cfg.thread_cnt));
795 } else {
796 PJ_LOG(4,(THIS_FILE, "No SIP worker threads created"));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000797 }
798
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000799 /* Done! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000800
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000801 PJ_LOG(3,(THIS_FILE, "pjsua version %s for %s initialized",
Benny Prijono106f5b72007-08-30 13:49:33 +0000802 pj_get_version(), PJ_OS_NAME));
Benny Prijonoccf95622006-02-07 18:48:01 +0000803
Benny Prijono268ca612006-02-07 12:34:11 +0000804 return PJ_SUCCESS;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000805
806on_error:
807 pjsua_destroy();
808 return status;
Benny Prijono268ca612006-02-07 12:34:11 +0000809}
810
811
Benny Prijono834aee32006-02-19 01:38:06 +0000812/* Sleep with polling */
813static void busy_sleep(unsigned msec)
814{
Benny Prijonob2c96822007-05-03 13:31:21 +0000815#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
Benny Prijono897f9f82007-05-03 19:56:21 +0000816 /* Ideally we shouldn't call pj_thread_sleep() and rather
817 * CActiveScheduler::WaitForAnyRequest() here, but that will
818 * drag in Symbian header and it doesn't look pretty.
819 */
Benny Prijonob2c96822007-05-03 13:31:21 +0000820 pj_thread_sleep(msec);
821#else
Benny Prijono834aee32006-02-19 01:38:06 +0000822 pj_time_val timeout, now;
823
824 pj_gettimeofday(&timeout);
825 timeout.msec += msec;
826 pj_time_val_normalize(&timeout);
827
828 do {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000829 while (pjsua_handle_events(10) > 0)
830 ;
Benny Prijono834aee32006-02-19 01:38:06 +0000831 pj_gettimeofday(&now);
832 } while (PJ_TIME_VAL_LT(now, timeout));
Benny Prijonob2c96822007-05-03 13:31:21 +0000833#endif
Benny Prijono834aee32006-02-19 01:38:06 +0000834}
835
Benny Prijonoebbf6892007-03-24 17:37:25 +0000836
Benny Prijonoebbf6892007-03-24 17:37:25 +0000837/*
838 * Callback function to receive notification from the resolver
839 * when the resolution process completes.
840 */
841static void stun_dns_srv_resolver_cb(void *user_data,
842 pj_status_t status,
843 const pj_dns_srv_record *rec)
844{
845 unsigned i;
846
847 PJ_UNUSED_ARG(user_data);
848
849 pjsua_var.stun_status = status;
850
851 if (status != PJ_SUCCESS) {
852 /* DNS SRV resolution failed. If stun_host is specified, resolve
853 * it with gethostbyname()
854 */
855 if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000856 pj_str_t str_host, str_port;
857 int port;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000858 pj_hostent he;
859
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000860 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
861 if (str_port.ptr != NULL) {
862 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
863 str_host.slen = (str_port.ptr - str_host.ptr);
864 str_port.ptr++;
865 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
866 str_host.slen - 1;
867 port = (int)pj_strtoul(&str_port);
868 if (port < 1 || port > 65535) {
869 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
870 pjsua_var.stun_status = PJ_EINVAL;
871 return;
872 }
873 } else {
874 str_host = pjsua_var.ua_cfg.stun_host;
875 port = 3478;
876 }
877
878 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000879
880 if (pjsua_var.stun_status == PJ_SUCCESS) {
881 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
882 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000883 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000884
885 PJ_LOG(3,(THIS_FILE,
886 "STUN server %.*s resolved, address is %s:%d",
887 (int)pjsua_var.ua_cfg.stun_host.slen,
888 pjsua_var.ua_cfg.stun_host.ptr,
889 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
890 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
891 }
892 } else {
893 char errmsg[PJ_ERR_MSG_SIZE];
894
895 pj_strerror(status, errmsg, sizeof(errmsg));
896 PJ_LOG(1,(THIS_FILE,
897 "DNS SRV resolution failed for STUN server %.*s: %s",
898 (int)pjsua_var.ua_cfg.stun_domain.slen,
899 pjsua_var.ua_cfg.stun_domain.ptr,
900 errmsg));
901 }
902 return;
903 }
904
Benny Prijonof5afa922007-06-11 16:51:18 +0000905 pj_assert(rec->count != 0 && rec->entry[0].server.addr_count != 0);
906 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL,
907 rec->entry[0].port);
908 pjsua_var.stun_srv.ipv4.sin_addr.s_addr =
909 rec->entry[0].server.addr[0].s_addr;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000910
911 PJ_LOG(3,(THIS_FILE, "_stun._udp.%.*s resolved, found %d entry(s):",
912 (int)pjsua_var.ua_cfg.stun_domain.slen,
913 pjsua_var.ua_cfg.stun_domain.ptr,
914 rec->count));
915
916 for (i=0; i<rec->count; ++i) {
917 PJ_LOG(3,(THIS_FILE,
918 " %d: prio=%d, weight=%d %s:%d",
919 i, rec->entry[i].priority, rec->entry[i].weight,
Benny Prijonof5afa922007-06-11 16:51:18 +0000920 pj_inet_ntoa(rec->entry[i].server.addr[0]),
921 (int)rec->entry[i].port));
Benny Prijonoebbf6892007-03-24 17:37:25 +0000922 }
923
924}
925
Benny Prijono268ca612006-02-07 12:34:11 +0000926/*
Benny Prijonoc97608e2007-03-23 16:34:20 +0000927 * Resolve STUN server.
928 */
929pj_status_t pjsua_resolve_stun_server(pj_bool_t wait)
930{
931 if (pjsua_var.stun_status == PJ_EUNKNOWN) {
932 /* Initialize STUN configuration */
933 pj_stun_config_init(&pjsua_var.stun_cfg, &pjsua_var.cp.factory, 0,
934 pjsip_endpt_get_ioqueue(pjsua_var.endpt),
935 pjsip_endpt_get_timer_heap(pjsua_var.endpt));
936
937 /* Start STUN server resolution */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000938
939 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000940
Benny Prijonoebbf6892007-03-24 17:37:25 +0000941 /* If stun_domain is specified, resolve STUN servers with DNS
942 * SRV resolution.
943 */
944 if (pjsua_var.ua_cfg.stun_domain.slen) {
945 pj_str_t res_type;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000946 pj_status_t status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000947
948 /* Fail if resolver is not configured */
949 if (pjsua_var.resolver == NULL) {
950 PJ_LOG(1,(THIS_FILE, "Nameserver must be configured when "
951 "stun_domain is specified"));
952 pjsua_var.stun_status = PJLIB_UTIL_EDNSNONS;
953 return PJLIB_UTIL_EDNSNONS;
954 }
955 res_type = pj_str("_stun._udp");
Benny Prijonoda9785b2007-04-02 20:43:06 +0000956 status =
Benny Prijonoebbf6892007-03-24 17:37:25 +0000957 pj_dns_srv_resolve(&pjsua_var.ua_cfg.stun_domain, &res_type,
958 3478, pjsua_var.pool, pjsua_var.resolver,
Benny Prijonof5afa922007-06-11 16:51:18 +0000959 0, NULL, &stun_dns_srv_resolver_cb, NULL);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000960 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +0000961 pjsua_perror(THIS_FILE, "Error starting DNS SRV resolution",
962 pjsua_var.stun_status);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000963 pjsua_var.stun_status = status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000964 return pjsua_var.stun_status;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000965 } else {
966 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000967 }
968 }
969 /* Otherwise if stun_host is specified, resolve STUN server with
970 * gethostbyname().
971 */
972 else if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000973 pj_str_t str_host, str_port;
974 int port;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000975 pj_hostent he;
976
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000977 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
978 if (str_port.ptr != NULL) {
979 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
980 str_host.slen = (str_port.ptr - str_host.ptr);
981 str_port.ptr++;
982 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
983 str_host.slen - 1;
984 port = (int)pj_strtoul(&str_port);
985 if (port < 1 || port > 65535) {
986 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
987 pjsua_var.stun_status = PJ_EINVAL;
988 return pjsua_var.stun_status;
989 }
990 } else {
991 str_host = pjsua_var.ua_cfg.stun_host;
992 port = 3478;
993 }
994
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000995 pjsua_var.stun_status =
996 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, &str_host,
997 (pj_uint16_t)port);
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000998
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000999 if (pjsua_var.stun_status != PJ_SUCCESS) {
1000 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001001
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001002 if (pjsua_var.stun_status == PJ_SUCCESS) {
1003 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
1004 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
1005 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
1006 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001007 }
Benny Prijonoebbf6892007-03-24 17:37:25 +00001008
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001009 PJ_LOG(3,(THIS_FILE,
1010 "STUN server %.*s resolved, address is %s:%d",
1011 (int)pjsua_var.ua_cfg.stun_host.slen,
1012 pjsua_var.ua_cfg.stun_host.ptr,
1013 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
1014 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
1015
Benny Prijonoc97608e2007-03-23 16:34:20 +00001016 }
Benny Prijonoebbf6892007-03-24 17:37:25 +00001017 /* Otherwise disable STUN. */
1018 else {
1019 pjsua_var.stun_status = PJ_SUCCESS;
1020 }
1021
1022
Benny Prijonoc97608e2007-03-23 16:34:20 +00001023 return pjsua_var.stun_status;
1024
1025 } else if (pjsua_var.stun_status == PJ_EPENDING) {
1026 /* STUN server resolution has been started, wait for the
1027 * result.
1028 */
Benny Prijonoebbf6892007-03-24 17:37:25 +00001029 if (wait) {
1030 while (pjsua_var.stun_status == PJ_EPENDING)
1031 pjsua_handle_events(10);
1032 }
1033
1034 return pjsua_var.stun_status;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001035
1036 } else {
1037 /* STUN server has been resolved, return the status */
1038 return pjsua_var.stun_status;
1039 }
1040}
1041
1042/*
Benny Prijono268ca612006-02-07 12:34:11 +00001043 * Destroy pjsua.
1044 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001045PJ_DEF(pj_status_t) pjsua_destroy(void)
Benny Prijono268ca612006-02-07 12:34:11 +00001046{
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001047 int i; /* Must be signed */
Benny Prijono268ca612006-02-07 12:34:11 +00001048
1049 /* Signal threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001050 pjsua_var.thread_quit_flag = 1;
Benny Prijono268ca612006-02-07 12:34:11 +00001051
1052 /* Wait worker threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001053 for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) {
1054 if (pjsua_var.thread[i]) {
1055 pj_thread_join(pjsua_var.thread[i]);
1056 pj_thread_destroy(pjsua_var.thread[i]);
1057 pjsua_var.thread[i] = NULL;
Benny Prijonoe4f2abb2006-02-10 14:04:05 +00001058 }
Benny Prijono268ca612006-02-07 12:34:11 +00001059 }
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001060
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001061 if (pjsua_var.endpt) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001062 /* Terminate all calls. */
1063 pjsua_call_hangup_all();
1064
1065 /* Terminate all presence subscriptions. */
1066 pjsua_pres_shutdown();
1067
1068 /* Unregister, if required: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001069 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1070 if (!pjsua_var.acc[i].valid)
1071 continue;
1072
1073 if (pjsua_var.acc[i].regc) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001074 pjsua_acc_set_registration(i, PJ_FALSE);
1075 }
1076 }
1077
1078 /* Wait for some time to allow unregistration to complete: */
Benny Prijono9fc735d2006-05-28 14:58:12 +00001079 PJ_LOG(4,(THIS_FILE, "Shutting down..."));
1080 busy_sleep(1000);
1081 }
Benny Prijono834aee32006-02-19 01:38:06 +00001082
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001083 /* Destroy media */
1084 pjsua_media_subsys_destroy();
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001085
Benny Prijono268ca612006-02-07 12:34:11 +00001086 /* Destroy endpoint. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001087 if (pjsua_var.endpt) {
1088 pjsip_endpt_destroy(pjsua_var.endpt);
1089 pjsua_var.endpt = NULL;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001090 }
Benny Prijono268ca612006-02-07 12:34:11 +00001091
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001092 /* Destroy mutex */
1093 if (pjsua_var.mutex) {
1094 pj_mutex_destroy(pjsua_var.mutex);
1095 pjsua_var.mutex = NULL;
1096 }
1097
1098 /* Destroy pool and pool factory. */
1099 if (pjsua_var.pool) {
1100 pj_pool_release(pjsua_var.pool);
1101 pjsua_var.pool = NULL;
1102 pj_caching_pool_destroy(&pjsua_var.cp);
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00001103
1104 PJ_LOG(4,(THIS_FILE, "PJSUA destroyed..."));
1105
1106 /* End logging */
1107 if (pjsua_var.log_file) {
1108 pj_file_close(pjsua_var.log_file);
1109 pjsua_var.log_file = NULL;
1110 }
1111
1112 /* Shutdown PJLIB */
1113 pj_shutdown();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001114 }
Benny Prijono268ca612006-02-07 12:34:11 +00001115
Benny Prijonof762ee72006-12-01 11:14:37 +00001116 /* Clear pjsua_var */
1117 pj_bzero(&pjsua_var, sizeof(pjsua_var));
1118
Benny Prijono268ca612006-02-07 12:34:11 +00001119 /* Done. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001120 return PJ_SUCCESS;
1121}
1122
1123
1124/**
1125 * Application is recommended to call this function after all initialization
1126 * is done, so that the library can do additional checking set up
1127 * additional
1128 *
1129 * @return PJ_SUCCESS on success, or the appropriate error code.
1130 */
1131PJ_DEF(pj_status_t) pjsua_start(void)
1132{
1133 pj_status_t status;
1134
1135 status = pjsua_call_subsys_start();
1136 if (status != PJ_SUCCESS)
1137 return status;
1138
1139 status = pjsua_media_subsys_start();
1140 if (status != PJ_SUCCESS)
1141 return status;
1142
1143 status = pjsua_pres_start();
1144 if (status != PJ_SUCCESS)
1145 return status;
Benny Prijono268ca612006-02-07 12:34:11 +00001146
1147 return PJ_SUCCESS;
1148}
1149
Benny Prijono9fc735d2006-05-28 14:58:12 +00001150
1151/**
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001152 * Poll pjsua for events, and if necessary block the caller thread for
1153 * the specified maximum interval (in miliseconds).
1154 */
1155PJ_DEF(int) pjsua_handle_events(unsigned msec_timeout)
1156{
Benny Prijono897f9f82007-05-03 19:56:21 +00001157#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
1158 /* Ideally we shouldn't call pj_thread_sleep() and rather
1159 * CActiveScheduler::WaitForAnyRequest() here, but that will
1160 * drag in Symbian header and it doesn't look pretty.
1161 */
1162 pj_thread_sleep(msec_timeout);
1163 return msec_timeout;
1164#else
1165
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001166 unsigned count = 0;
1167 pj_time_val tv;
1168 pj_status_t status;
1169
1170 tv.sec = 0;
1171 tv.msec = msec_timeout;
1172 pj_time_val_normalize(&tv);
1173
1174 status = pjsip_endpt_handle_events2(pjsua_var.endpt, &tv, &count);
1175
1176 if (status != PJ_SUCCESS)
1177 return -status;
1178
1179 return count;
Benny Prijono897f9f82007-05-03 19:56:21 +00001180#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001181}
1182
1183
1184/*
1185 * Create memory pool.
1186 */
1187PJ_DEF(pj_pool_t*) pjsua_pool_create( const char *name, pj_size_t init_size,
1188 pj_size_t increment)
1189{
1190 /* Pool factory is thread safe, no need to lock */
1191 return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment,
1192 NULL);
1193}
1194
1195
1196/*
1197 * Internal function to get SIP endpoint instance of pjsua, which is
1198 * needed for example to register module, create transports, etc.
1199 * Probably is only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001200 */
1201PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void)
1202{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001203 return pjsua_var.endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001204}
1205
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001206/*
1207 * Internal function to get media endpoint instance.
1208 * Only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001209 */
1210PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void)
1211{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001212 return pjsua_var.med_endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001213}
1214
Benny Prijono97b87172006-08-24 14:25:14 +00001215/*
1216 * Internal function to get PJSUA pool factory.
1217 */
1218PJ_DEF(pj_pool_factory*) pjsua_get_pool_factory(void)
1219{
1220 return &pjsua_var.cp.factory;
1221}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001222
1223/*****************************************************************************
1224 * PJSUA SIP Transport API.
1225 */
1226
1227/*
Benny Prijono23674a32007-12-01 08:59:25 +00001228 * Tools to get address string.
1229 */
1230static const char *addr_string(const pj_sockaddr_t *addr)
1231{
1232 static char str[128];
1233 str[0] = '\0';
1234 pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family,
1235 pj_sockaddr_get_addr(addr),
1236 str, sizeof(str));
1237 return str;
1238}
1239
1240/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001241 * Create and initialize SIP socket (and possibly resolve public
1242 * address via STUN, depending on config).
1243 */
Benny Prijono23674a32007-12-01 08:59:25 +00001244static pj_status_t create_sip_udp_sock(int af,
1245 const pj_str_t *bind_param,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001246 int port,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001247 pj_sock_t *p_sock,
Benny Prijono23674a32007-12-01 08:59:25 +00001248 pj_sockaddr *p_pub_addr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001249{
Benny Prijono23674a32007-12-01 08:59:25 +00001250 char stun_ip_addr[PJ_INET6_ADDRSTRLEN];
Benny Prijonoc97608e2007-03-23 16:34:20 +00001251 pj_str_t stun_srv;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001252 pj_sock_t sock;
Benny Prijono23674a32007-12-01 08:59:25 +00001253 pj_sockaddr bind_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001254 pj_status_t status;
1255
Benny Prijonoc97608e2007-03-23 16:34:20 +00001256 /* Make sure STUN server resolution has completed */
1257 status = pjsua_resolve_stun_server(PJ_TRUE);
1258 if (status != PJ_SUCCESS) {
1259 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
1260 return status;
1261 }
1262
Benny Prijono23674a32007-12-01 08:59:25 +00001263 /* Initialize bound address */
1264 if (bind_param->slen) {
1265 status = pj_sockaddr_init(af, &bind_addr, bind_param,
1266 (pj_uint16_t)port);
1267 if (status != PJ_SUCCESS) {
1268 pjsua_perror(THIS_FILE,
1269 "Unable to resolve transport bound address",
1270 status);
1271 return status;
1272 }
1273 } else {
1274 pj_sockaddr_init(af, &bind_addr, NULL, (pj_uint16_t)port);
1275 }
1276
1277 status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001278 if (status != PJ_SUCCESS) {
1279 pjsua_perror(THIS_FILE, "socket() error", status);
Benny Prijonod8410532006-06-15 11:04:33 +00001280 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001281 }
1282
Benny Prijono5f55a1f2007-12-05 05:08:29 +00001283 status = pj_sock_bind(sock, &bind_addr, pj_sockaddr_get_len(&bind_addr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001284 if (status != PJ_SUCCESS) {
1285 pjsua_perror(THIS_FILE, "bind() error", status);
1286 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001287 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001288 }
1289
Benny Prijonoe347cb02007-02-14 14:36:13 +00001290 /* If port is zero, get the bound port */
1291 if (port == 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001292 pj_sockaddr bound_addr;
Benny Prijonoe347cb02007-02-14 14:36:13 +00001293 int namelen = sizeof(bound_addr);
1294 status = pj_sock_getsockname(sock, &bound_addr, &namelen);
1295 if (status != PJ_SUCCESS) {
1296 pjsua_perror(THIS_FILE, "getsockname() error", status);
1297 pj_sock_close(sock);
1298 return status;
1299 }
1300
Benny Prijono23674a32007-12-01 08:59:25 +00001301 port = pj_sockaddr_get_port(&bound_addr);
Benny Prijonoe347cb02007-02-14 14:36:13 +00001302 }
1303
Benny Prijonoc97608e2007-03-23 16:34:20 +00001304 if (pjsua_var.stun_srv.addr.sa_family != 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001305 pj_ansi_strcpy(stun_ip_addr,pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
1306 stun_srv = pj_str(stun_ip_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001307 } else {
Benny Prijonoc97608e2007-03-23 16:34:20 +00001308 stun_srv.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001309 }
1310
1311 /* Get the published address, either by STUN or by resolving
1312 * the name of local host.
1313 */
Benny Prijono23674a32007-12-01 08:59:25 +00001314 if (pj_sockaddr_has_addr(p_pub_addr)) {
Benny Prijono744aca02007-11-11 03:06:07 +00001315 /*
1316 * Public address is already specified, no need to resolve the
1317 * address, only set the port.
1318 */
Benny Prijono23674a32007-12-01 08:59:25 +00001319 if (pj_sockaddr_get_port(p_pub_addr) == 0)
1320 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijono744aca02007-11-11 03:06:07 +00001321
1322 } else if (stun_srv.slen) {
Benny Prijono0a5cad82006-09-26 13:21:02 +00001323 /*
1324 * STUN is specified, resolve the address with STUN.
1325 */
Benny Prijono23674a32007-12-01 08:59:25 +00001326 if (af != pj_AF_INET()) {
1327 pjsua_perror(THIS_FILE, "Cannot use STUN", PJ_EAFNOTSUP);
1328 pj_sock_close(sock);
1329 return PJ_EAFNOTSUP;
1330 }
1331
Benny Prijono14c2b862007-02-21 00:40:05 +00001332 status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001333 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
1334 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
Benny Prijono23674a32007-12-01 08:59:25 +00001335 &p_pub_addr->ipv4);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001336 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +00001337 pjsua_perror(THIS_FILE, "Error contacting STUN server", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001338 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001339 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001340 }
1341
1342 } else {
Benny Prijono23674a32007-12-01 08:59:25 +00001343 pj_bzero(p_pub_addr, sizeof(pj_sockaddr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001344
Benny Prijono42d08d22007-12-20 11:23:07 +00001345 if (pj_sockaddr_has_addr(&bind_addr)) {
1346 pj_sockaddr_copy_addr(p_pub_addr, &bind_addr);
1347 } else {
1348 status = pj_gethostip(af, p_pub_addr);
1349 if (status != PJ_SUCCESS) {
1350 pjsua_perror(THIS_FILE, "Unable to get local host IP", status);
1351 pj_sock_close(sock);
1352 return status;
1353 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001354 }
1355
Benny Prijono23674a32007-12-01 08:59:25 +00001356 p_pub_addr->addr.sa_family = (pj_uint16_t)af;
1357 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001358 }
1359
1360 *p_sock = sock;
1361
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001362 PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
Benny Prijono23674a32007-12-01 08:59:25 +00001363 addr_string(p_pub_addr),
1364 (int)pj_sockaddr_get_port(p_pub_addr)));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001365
Benny Prijonod8410532006-06-15 11:04:33 +00001366 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001367}
1368
1369
1370/*
1371 * Create SIP transport.
1372 */
1373PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
1374 const pjsua_transport_config *cfg,
1375 pjsua_transport_id *p_id)
1376{
1377 pjsip_transport *tp;
1378 unsigned id;
1379 pj_status_t status;
1380
1381 PJSUA_LOCK();
1382
1383 /* Find empty transport slot */
1384 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001385 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001386 break;
1387 }
1388
1389 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1390 status = PJ_ETOOMANY;
1391 pjsua_perror(THIS_FILE, "Error creating transport", status);
1392 goto on_return;
1393 }
1394
1395 /* Create the transport */
Benny Prijonob2477142007-12-05 04:09:59 +00001396 if (type==PJSIP_TRANSPORT_UDP || type==PJSIP_TRANSPORT_UDP6) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001397 /*
Benny Prijono23674a32007-12-01 08:59:25 +00001398 * Create UDP transport (IPv4 or IPv6).
Benny Prijonoe93e2872006-06-28 16:46:49 +00001399 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001400 pjsua_transport_config config;
Benny Prijono23674a32007-12-01 08:59:25 +00001401 char hostbuf[PJ_INET6_ADDRSTRLEN];
Benny Prijonod8410532006-06-15 11:04:33 +00001402 pj_sock_t sock = PJ_INVALID_SOCKET;
Benny Prijono23674a32007-12-01 08:59:25 +00001403 pj_sockaddr pub_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001404 pjsip_host_port addr_name;
1405
1406 /* Supply default config if it's not specified */
1407 if (cfg == NULL) {
1408 pjsua_transport_config_default(&config);
1409 cfg = &config;
1410 }
1411
Benny Prijono0a5cad82006-09-26 13:21:02 +00001412 /* Initialize the public address from the config, if any */
Benny Prijono23674a32007-12-01 08:59:25 +00001413 pj_sockaddr_init(pjsip_transport_type_get_af(type), &pub_addr,
1414 NULL, (pj_uint16_t)cfg->port);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001415 if (cfg->public_addr.slen) {
Benny Prijono23674a32007-12-01 08:59:25 +00001416 status = pj_sockaddr_set_str_addr(pjsip_transport_type_get_af(type),
1417 &pub_addr, &cfg->public_addr);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001418 if (status != PJ_SUCCESS) {
1419 pjsua_perror(THIS_FILE,
1420 "Unable to resolve transport public address",
1421 status);
1422 goto on_return;
1423 }
1424 }
1425
1426 /* Create the socket and possibly resolve the address with STUN
1427 * (only when public address is not specified).
1428 */
Benny Prijono23674a32007-12-01 08:59:25 +00001429 status = create_sip_udp_sock(pjsip_transport_type_get_af(type),
1430 &cfg->bound_addr, cfg->port,
Benny Prijono0a5cad82006-09-26 13:21:02 +00001431 &sock, &pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001432 if (status != PJ_SUCCESS)
1433 goto on_return;
1434
Benny Prijono23674a32007-12-01 08:59:25 +00001435 pj_ansi_strcpy(hostbuf, addr_string(&pub_addr));
1436 addr_name.host = pj_str(hostbuf);
1437 addr_name.port = pj_sockaddr_get_port(&pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001438
1439 /* Create UDP transport */
Benny Prijono23674a32007-12-01 08:59:25 +00001440 status = pjsip_udp_transport_attach2(pjsua_var.endpt, type, sock,
1441 &addr_name, 1, &tp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001442 if (status != PJ_SUCCESS) {
1443 pjsua_perror(THIS_FILE, "Error creating SIP UDP transport",
1444 status);
1445 pj_sock_close(sock);
1446 goto on_return;
1447 }
1448
Benny Prijonoe93e2872006-06-28 16:46:49 +00001449
1450 /* Save the transport */
1451 pjsua_var.tpdata[id].type = type;
1452 pjsua_var.tpdata[id].local_name = tp->local_name;
1453 pjsua_var.tpdata[id].data.tp = tp;
1454
Benny Prijono3569c0d2007-04-06 10:29:20 +00001455#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
1456
Benny Prijonob2477142007-12-05 04:09:59 +00001457 } else if (type == PJSIP_TRANSPORT_TCP || type == PJSIP_TRANSPORT_TCP6) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001458 /*
1459 * Create TCP transport.
1460 */
1461 pjsua_transport_config config;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001462 pjsip_host_port a_name;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001463 pjsip_tpfactory *tcp;
1464 pj_sockaddr_in local_addr;
1465
1466 /* Supply default config if it's not specified */
1467 if (cfg == NULL) {
1468 pjsua_transport_config_default(&config);
1469 cfg = &config;
1470 }
1471
1472 /* Init local address */
1473 pj_sockaddr_in_init(&local_addr, 0, 0);
1474
1475 if (cfg->port)
1476 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1477
Benny Prijono0a5cad82006-09-26 13:21:02 +00001478 if (cfg->bound_addr.slen) {
1479 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1480 if (status != PJ_SUCCESS) {
1481 pjsua_perror(THIS_FILE,
1482 "Unable to resolve transport bound address",
1483 status);
1484 goto on_return;
1485 }
1486 }
1487
1488 /* Init published name */
1489 pj_bzero(&a_name, sizeof(pjsip_host_port));
1490 if (cfg->public_addr.slen)
1491 a_name.host = cfg->public_addr;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001492
1493 /* Create the TCP transport */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001494 status = pjsip_tcp_transport_start2(pjsua_var.endpt, &local_addr,
1495 &a_name, 1, &tcp);
Benny Prijonoe93e2872006-06-28 16:46:49 +00001496
1497 if (status != PJ_SUCCESS) {
1498 pjsua_perror(THIS_FILE, "Error creating SIP TCP listener",
1499 status);
1500 goto on_return;
1501 }
1502
1503 /* Save the transport */
1504 pjsua_var.tpdata[id].type = type;
1505 pjsua_var.tpdata[id].local_name = tcp->addr_name;
1506 pjsua_var.tpdata[id].data.factory = tcp;
1507
Benny Prijono3569c0d2007-04-06 10:29:20 +00001508#endif /* PJ_HAS_TCP */
1509
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001510#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
1511 } else if (type == PJSIP_TRANSPORT_TLS) {
1512 /*
1513 * Create TLS transport.
1514 */
Benny Prijonof3bbc132006-12-25 06:43:59 +00001515 /*
1516 * Create TCP transport.
1517 */
1518 pjsua_transport_config config;
1519 pjsip_host_port a_name;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001520 pjsip_tpfactory *tls;
Benny Prijonof3bbc132006-12-25 06:43:59 +00001521 pj_sockaddr_in local_addr;
1522
1523 /* Supply default config if it's not specified */
1524 if (cfg == NULL) {
1525 pjsua_transport_config_default(&config);
1526 config.port = 5061;
1527 cfg = &config;
1528 }
1529
1530 /* Init local address */
1531 pj_sockaddr_in_init(&local_addr, 0, 0);
1532
1533 if (cfg->port)
1534 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1535
1536 if (cfg->bound_addr.slen) {
1537 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1538 if (status != PJ_SUCCESS) {
1539 pjsua_perror(THIS_FILE,
1540 "Unable to resolve transport bound address",
1541 status);
1542 goto on_return;
1543 }
1544 }
1545
1546 /* Init published name */
1547 pj_bzero(&a_name, sizeof(pjsip_host_port));
1548 if (cfg->public_addr.slen)
1549 a_name.host = cfg->public_addr;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001550
1551 status = pjsip_tls_transport_start(pjsua_var.endpt,
Benny Prijonof3bbc132006-12-25 06:43:59 +00001552 &cfg->tls_setting,
1553 &local_addr, &a_name, 1, &tls);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001554 if (status != PJ_SUCCESS) {
1555 pjsua_perror(THIS_FILE, "Error creating SIP TLS listener",
1556 status);
1557 goto on_return;
1558 }
1559
1560 /* Save the transport */
1561 pjsua_var.tpdata[id].type = type;
1562 pjsua_var.tpdata[id].local_name = tls->addr_name;
1563 pjsua_var.tpdata[id].data.factory = tls;
1564#endif
1565
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001566 } else {
1567 status = PJSIP_EUNSUPTRANSPORT;
1568 pjsua_perror(THIS_FILE, "Error creating transport", status);
1569 goto on_return;
1570 }
1571
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001572
1573 /* Return the ID */
1574 if (p_id) *p_id = id;
1575
1576 status = PJ_SUCCESS;
1577
1578on_return:
1579
1580 PJSUA_UNLOCK();
1581
Benny Prijonod8410532006-06-15 11:04:33 +00001582 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001583}
1584
1585
1586/*
1587 * Register transport that has been created by application.
1588 */
1589PJ_DEF(pj_status_t) pjsua_transport_register( pjsip_transport *tp,
1590 pjsua_transport_id *p_id)
1591{
1592 unsigned id;
1593
1594 PJSUA_LOCK();
1595
1596 /* Find empty transport slot */
1597 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001598 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001599 break;
1600 }
1601
1602 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1603 pjsua_perror(THIS_FILE, "Error creating transport", PJ_ETOOMANY);
1604 PJSUA_UNLOCK();
1605 return PJ_ETOOMANY;
1606 }
1607
1608 /* Save the transport */
Benny Prijonod17a5e92007-05-29 11:51:45 +00001609 pjsua_var.tpdata[id].type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001610 pjsua_var.tpdata[id].local_name = tp->local_name;
1611 pjsua_var.tpdata[id].data.tp = tp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001612
1613 /* Return the ID */
1614 if (p_id) *p_id = id;
1615
1616 PJSUA_UNLOCK();
1617
1618 return PJ_SUCCESS;
1619}
1620
1621
1622/*
1623 * Enumerate all transports currently created in the system.
1624 */
1625PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[],
1626 unsigned *p_count )
1627{
1628 unsigned i, count;
1629
1630 PJSUA_LOCK();
1631
1632 for (i=0, count=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata) && count<*p_count;
1633 ++i)
1634 {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001635 if (!pjsua_var.tpdata[i].data.ptr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001636 continue;
1637
1638 id[count++] = i;
1639 }
1640
1641 *p_count = count;
1642
1643 PJSUA_UNLOCK();
1644
1645 return PJ_SUCCESS;
1646}
1647
1648
1649/*
1650 * Get information about transports.
1651 */
1652PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
1653 pjsua_transport_info *info)
1654{
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001655 pjsua_transport_data *t = &pjsua_var.tpdata[id];
Benny Prijono0a5cad82006-09-26 13:21:02 +00001656 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001657
Benny Prijonoac623b32006-07-03 15:19:31 +00001658 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001659
1660 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001661 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1662 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001663
1664 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001665 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001666
1667 PJSUA_LOCK();
1668
Benny Prijonoe93e2872006-06-28 16:46:49 +00001669 if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_UDP) {
1670
1671 pjsip_transport *tp = t->data.tp;
1672
1673 if (tp == NULL) {
1674 PJSUA_UNLOCK();
1675 return PJ_EINVALIDOP;
1676 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001677
Benny Prijonoe93e2872006-06-28 16:46:49 +00001678 info->id = id;
Benny Prijonod17a5e92007-05-29 11:51:45 +00001679 info->type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001680 info->type_name = pj_str(tp->type_name);
1681 info->info = pj_str(tp->info);
1682 info->flag = tp->flag;
1683 info->addr_len = tp->addr_len;
1684 info->local_addr = tp->local_addr;
1685 info->local_name = tp->local_name;
1686 info->usage_count = pj_atomic_get(tp->ref_cnt);
1687
Benny Prijono0a5cad82006-09-26 13:21:02 +00001688 status = PJ_SUCCESS;
1689
Benny Prijonoe93e2872006-06-28 16:46:49 +00001690 } else if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_TCP) {
1691
1692 pjsip_tpfactory *factory = t->data.factory;
1693
1694 if (factory == NULL) {
1695 PJSUA_UNLOCK();
1696 return PJ_EINVALIDOP;
1697 }
1698
1699 info->id = id;
1700 info->type = t->type;
1701 info->type_name = pj_str("TCP");
1702 info->info = pj_str("TCP transport");
1703 info->flag = factory->flag;
1704 info->addr_len = sizeof(factory->local_addr);
1705 info->local_addr = factory->local_addr;
1706 info->local_name = factory->addr_name;
1707 info->usage_count = 0;
1708
Benny Prijono0a5cad82006-09-26 13:21:02 +00001709 status = PJ_SUCCESS;
1710
1711 } else {
1712 pj_assert(!"Unsupported transport");
1713 status = PJ_EINVALIDOP;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001714 }
1715
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001716
1717 PJSUA_UNLOCK();
1718
Benny Prijono0a5cad82006-09-26 13:21:02 +00001719 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001720}
1721
1722
1723/*
1724 * Disable a transport or re-enable it.
1725 */
1726PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id,
1727 pj_bool_t enabled)
1728{
1729 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001730 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1731 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001732
1733 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001734 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001735
1736
1737 /* To be done!! */
1738 PJ_TODO(pjsua_transport_set_enable);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001739 PJ_UNUSED_ARG(enabled);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001740
1741 return PJ_EINVALIDOP;
1742}
1743
1744
1745/*
1746 * Close the transport.
1747 */
1748PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
1749 pj_bool_t force )
1750{
Benny Prijono5ff61872007-02-01 03:37:11 +00001751 pj_status_t status;
1752
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001753 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001754 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1755 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001756
1757 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001758 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001759
Benny Prijono0a5cad82006-09-26 13:21:02 +00001760 /* Note: destroy() may not work if there are objects still referencing
1761 * the transport.
1762 */
1763 if (force) {
1764 switch (pjsua_var.tpdata[id].type) {
1765 case PJSIP_TRANSPORT_UDP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001766 status = pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
1767 if (status != PJ_SUCCESS)
1768 return status;
1769 status = pjsip_transport_destroy(pjsua_var.tpdata[id].data.tp);
1770 if (status != PJ_SUCCESS)
1771 return status;
1772 break;
1773
1774 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001775 case PJSIP_TRANSPORT_TCP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001776 /* This will close the TCP listener, but existing TCP/TLS
1777 * connections (if any) will still linger
1778 */
1779 status = (*pjsua_var.tpdata[id].data.factory->destroy)
1780 (pjsua_var.tpdata[id].data.factory);
1781 if (status != PJ_SUCCESS)
1782 return status;
1783
Benny Prijono0a5cad82006-09-26 13:21:02 +00001784 break;
Benny Prijono5ff61872007-02-01 03:37:11 +00001785
Benny Prijono1ebd6142006-10-19 15:48:02 +00001786 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001787 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001788 }
1789
1790 } else {
Benny Prijono5ff61872007-02-01 03:37:11 +00001791 /* If force is not specified, transports will be closed at their
1792 * convenient time. However this will leak PJSUA-API transport
1793 * descriptors as PJSUA-API wouldn't know when exactly the
1794 * transport is closed thus it can't cleanup PJSUA transport
1795 * descriptor.
1796 */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001797 switch (pjsua_var.tpdata[id].type) {
1798 case PJSIP_TRANSPORT_UDP:
1799 return pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
Benny Prijono5ff61872007-02-01 03:37:11 +00001800 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001801 case PJSIP_TRANSPORT_TCP:
1802 return (*pjsua_var.tpdata[id].data.factory->destroy)
1803 (pjsua_var.tpdata[id].data.factory);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001804 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001805 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001806 }
1807 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001808
Benny Prijono5ff61872007-02-01 03:37:11 +00001809 /* Cleanup pjsua data when force is applied */
1810 if (force) {
1811 pjsua_var.tpdata[id].type = PJSIP_TRANSPORT_UNSPECIFIED;
1812 pjsua_var.tpdata[id].data.ptr = NULL;
1813 }
1814
1815 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001816}
1817
1818
1819/*
1820 * Add additional headers etc in msg_data specified by application
1821 * when sending requests.
1822 */
1823void pjsua_process_msg_data(pjsip_tx_data *tdata,
1824 const pjsua_msg_data *msg_data)
1825{
1826 pj_bool_t allow_body;
1827 const pjsip_hdr *hdr;
1828
Benny Prijono56315612006-07-18 14:39:40 +00001829 /* Always add User-Agent */
1830 if (pjsua_var.ua_cfg.user_agent.slen &&
1831 tdata->msg->type == PJSIP_REQUEST_MSG)
1832 {
1833 const pj_str_t STR_USER_AGENT = { "User-Agent", 10 };
1834 pjsip_hdr *h;
1835 h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool,
1836 &STR_USER_AGENT,
1837 &pjsua_var.ua_cfg.user_agent);
1838 pjsip_msg_add_hdr(tdata->msg, h);
1839 }
1840
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001841 if (!msg_data)
1842 return;
1843
1844 hdr = msg_data->hdr_list.next;
1845 while (hdr && hdr != &msg_data->hdr_list) {
1846 pjsip_hdr *new_hdr;
1847
Benny Prijonoa1e69682007-05-11 15:14:34 +00001848 new_hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001849 pjsip_msg_add_hdr(tdata->msg, new_hdr);
1850
1851 hdr = hdr->next;
1852 }
1853
1854 allow_body = (tdata->msg->body == NULL);
1855
1856 if (allow_body && msg_data->content_type.slen && msg_data->msg_body.slen) {
1857 pjsip_media_type ctype;
1858 pjsip_msg_body *body;
1859
1860 pjsua_parse_media_type(tdata->pool, &msg_data->content_type, &ctype);
1861 body = pjsip_msg_body_create(tdata->pool, &ctype.type, &ctype.subtype,
1862 &msg_data->msg_body);
1863 tdata->msg->body = body;
1864 }
1865}
1866
1867
1868/*
1869 * Add route_set to outgoing requests
1870 */
1871void pjsua_set_msg_route_set( pjsip_tx_data *tdata,
1872 const pjsip_route_hdr *route_set )
1873{
1874 const pjsip_route_hdr *r;
1875
1876 r = route_set->next;
1877 while (r != route_set) {
1878 pjsip_route_hdr *new_r;
1879
Benny Prijonoa1e69682007-05-11 15:14:34 +00001880 new_r = (pjsip_route_hdr*) pjsip_hdr_clone(tdata->pool, r);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001881 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)new_r);
1882
1883 r = r->next;
1884 }
1885}
1886
1887
1888/*
1889 * Simple version of MIME type parsing (it doesn't support parameters)
1890 */
1891void pjsua_parse_media_type( pj_pool_t *pool,
1892 const pj_str_t *mime,
1893 pjsip_media_type *media_type)
1894{
1895 pj_str_t tmp;
1896 char *pos;
1897
Benny Prijonoac623b32006-07-03 15:19:31 +00001898 pj_bzero(media_type, sizeof(*media_type));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001899
1900 pj_strdup_with_null(pool, &tmp, mime);
1901
1902 pos = pj_strchr(&tmp, '/');
1903 if (pos) {
1904 media_type->type.ptr = tmp.ptr;
1905 media_type->type.slen = (pos-tmp.ptr);
1906 media_type->subtype.ptr = pos+1;
1907 media_type->subtype.slen = tmp.ptr+tmp.slen-pos-1;
1908 } else {
1909 media_type->type = tmp;
1910 }
1911}
1912
1913
1914/*
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001915 * Internal function to init transport selector from transport id.
1916 */
1917void pjsua_init_tpselector(pjsua_transport_id tp_id,
1918 pjsip_tpselector *sel)
1919{
1920 pjsua_transport_data *tpdata;
1921 unsigned flag;
1922
1923 pj_bzero(sel, sizeof(*sel));
1924 if (tp_id == PJSUA_INVALID_ID)
1925 return;
1926
Benny Prijonoa1e69682007-05-11 15:14:34 +00001927 pj_assert(tp_id >= 0 && tp_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata));
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001928 tpdata = &pjsua_var.tpdata[tp_id];
1929
1930 flag = pjsip_transport_get_flag_from_type(tpdata->type);
1931
1932 if (flag & PJSIP_TRANSPORT_DATAGRAM) {
1933 sel->type = PJSIP_TPSELECTOR_TRANSPORT;
1934 sel->u.transport = tpdata->data.tp;
1935 } else {
1936 sel->type = PJSIP_TPSELECTOR_LISTENER;
1937 sel->u.listener = tpdata->data.factory;
1938 }
1939}
1940
1941
Benny Prijono6ba8c542007-10-16 01:34:14 +00001942/* Callback upon NAT detection completion */
1943static void nat_detect_cb(void *user_data,
1944 const pj_stun_nat_detect_result *res)
1945{
1946 PJ_UNUSED_ARG(user_data);
1947
1948 pjsua_var.nat_in_progress = PJ_FALSE;
1949 pjsua_var.nat_status = res->status;
1950 pjsua_var.nat_type = res->nat_type;
1951
1952 if (pjsua_var.ua_cfg.cb.on_nat_detect) {
1953 (*pjsua_var.ua_cfg.cb.on_nat_detect)(res);
1954 }
1955}
1956
1957
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001958/*
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001959 * Detect NAT type.
1960 */
Benny Prijono6ba8c542007-10-16 01:34:14 +00001961PJ_DEF(pj_status_t) pjsua_detect_nat_type()
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001962{
1963 pj_status_t status;
1964
Benny Prijono6ba8c542007-10-16 01:34:14 +00001965 if (pjsua_var.nat_in_progress)
1966 return PJ_SUCCESS;
1967
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001968 /* Make sure STUN server resolution has completed */
1969 status = pjsua_resolve_stun_server(PJ_TRUE);
1970 if (status != PJ_SUCCESS) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00001971 pjsua_var.nat_status = status;
1972 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001973 return status;
1974 }
1975
1976 /* Make sure we have STUN */
1977 if (pjsua_var.stun_srv.ipv4.sin_family == 0) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00001978 pjsua_var.nat_status = PJNATH_ESTUNINSERVER;
1979 return PJNATH_ESTUNINSERVER;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001980 }
1981
Benny Prijono6ba8c542007-10-16 01:34:14 +00001982 status = pj_stun_detect_nat_type(&pjsua_var.stun_srv.ipv4,
1983 &pjsua_var.stun_cfg,
1984 NULL, &nat_detect_cb);
1985
1986 if (status != PJ_SUCCESS) {
1987 pjsua_var.nat_status = status;
1988 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
1989 return status;
1990 }
1991
1992 pjsua_var.nat_in_progress = PJ_TRUE;
1993
1994 return PJ_SUCCESS;
1995}
1996
1997
1998/*
1999 * Get NAT type.
2000 */
2001PJ_DEF(pj_status_t) pjsua_get_nat_type(pj_stun_nat_type *type)
2002{
2003 *type = pjsua_var.nat_type;
2004 return pjsua_var.nat_status;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002005}
2006
2007
2008/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002009 * Verify that valid SIP url is given.
2010 */
2011PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url)
2012{
2013 pjsip_uri *p;
2014 pj_pool_t *pool;
2015 char *url;
2016 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
2017
2018 if (!len) return -1;
2019
2020 pool = pj_pool_create(&pjsua_var.cp.factory, "check%p", 1024, 0, NULL);
2021 if (!pool) return -1;
2022
Benny Prijonoa1e69682007-05-11 15:14:34 +00002023 url = (char*) pj_pool_alloc(pool, len+1);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002024 pj_ansi_strcpy(url, c_url);
2025
2026 p = pjsip_parse_uri(pool, url, len, 0);
Benny Prijonocf0b4b22007-10-06 17:31:09 +00002027 if (!p || (pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0 &&
2028 pj_stricmp2(pjsip_uri_get_scheme(p), "sips") != 0))
2029 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002030 p = NULL;
Benny Prijonocf0b4b22007-10-06 17:31:09 +00002031 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002032
2033 pj_pool_release(pool);
2034 return p ? 0 : -1;
2035}
Benny Prijonoda9785b2007-04-02 20:43:06 +00002036
2037
2038/*
2039 * This is a utility function to dump the stack states to log, using
2040 * verbosity level 3.
2041 */
2042PJ_DEF(void) pjsua_dump(pj_bool_t detail)
2043{
2044 unsigned old_decor;
2045 unsigned i;
2046 char buf[1024];
2047
2048 PJ_LOG(3,(THIS_FILE, "Start dumping application states:"));
2049
2050 old_decor = pj_log_get_decor();
2051 pj_log_set_decor(old_decor & (PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
2052
2053 if (detail)
2054 pj_dump_config();
2055
2056 pjsip_endpt_dump(pjsua_get_pjsip_endpt(), detail);
2057
2058 pjmedia_endpt_dump(pjsua_get_pjmedia_endpt());
2059
2060 PJ_LOG(3,(THIS_FILE, "Dumping media transports:"));
2061 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2062 pjsua_call *call = &pjsua_var.calls[i];
2063 pjmedia_sock_info skinfo;
Benny Prijono5186eae2007-12-03 14:38:25 +00002064 char addr_buf[80];
Benny Prijonoda9785b2007-04-02 20:43:06 +00002065
Benny Prijono4f093d22007-04-09 08:54:32 +00002066 /* MSVC complains about skinfo not being initialized */
2067 pj_bzero(&skinfo, sizeof(skinfo));
2068
Benny Prijonoda9785b2007-04-02 20:43:06 +00002069 pjmedia_transport_get_info(call->med_tp, &skinfo);
2070
Benny Prijono5186eae2007-12-03 14:38:25 +00002071 PJ_LOG(3,(THIS_FILE, " %s: %s",
Benny Prijonoda9785b2007-04-02 20:43:06 +00002072 (pjsua_var.media_cfg.enable_ice ? "ICE" : "UDP"),
Benny Prijono5186eae2007-12-03 14:38:25 +00002073 pj_sockaddr_print(&skinfo.rtp_addr_name, addr_buf,
2074 sizeof(addr_buf), 3)));
Benny Prijonoda9785b2007-04-02 20:43:06 +00002075 }
2076
2077 pjsip_tsx_layer_dump(detail);
2078 pjsip_ua_dump(detail);
2079
2080
2081 /* Dump all invite sessions: */
2082 PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
2083
2084 if (pjsua_call_get_count() == 0) {
2085
2086 PJ_LOG(3,(THIS_FILE, " - no sessions -"));
2087
2088 } else {
2089 unsigned i;
2090
2091 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2092 if (pjsua_call_is_active(i)) {
2093 pjsua_call_dump(i, detail, buf, sizeof(buf), " ");
2094 PJ_LOG(3,(THIS_FILE, "%s", buf));
2095 }
2096 }
2097 }
2098
2099 /* Dump presence status */
2100 pjsua_pres_dump(detail);
2101
2102 pj_log_set_decor(old_decor);
2103 PJ_LOG(3,(THIS_FILE, "Dump complete"));
2104}
2105