blob: 7304df06b261279a253445eb41b9a681d6a57d21 [file] [log] [blame]
Benny Prijono268ca612006-02-07 12:34:11 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono268ca612006-02-07 12:34:11 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000020#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000021#include <pjsua-lib/pjsua_internal.h>
Benny Prijono268ca612006-02-07 12:34:11 +000022
Benny Prijono268ca612006-02-07 12:34:11 +000023
Benny Prijono84126ab2006-02-09 09:30:09 +000024#define THIS_FILE "pjsua_core.c"
Benny Prijono268ca612006-02-07 12:34:11 +000025
26
Benny Prijonoeebe9af2006-06-13 22:57:13 +000027/* PJSUA application instance. */
28struct pjsua_data pjsua_var;
Benny Prijono84126ab2006-02-09 09:30:09 +000029
30
Benny Prijono44e88ea2007-10-30 15:42:35 +000031PJ_DEF(struct pjsua_data*) pjsua_get_var(void)
32{
33 return &pjsua_var;
34}
35
36
Benny Prijonoeebe9af2006-06-13 22:57:13 +000037/* Display error */
38PJ_DEF(void) pjsua_perror( const char *sender, const char *title,
39 pj_status_t status)
Benny Prijono268ca612006-02-07 12:34:11 +000040{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000041 char errmsg[PJ_ERR_MSG_SIZE];
Benny Prijonoa91a0032006-02-26 21:23:45 +000042
Benny Prijonoeebe9af2006-06-13 22:57:13 +000043 pj_strerror(status, errmsg, sizeof(errmsg));
Benny Prijonod6e362a2008-07-19 17:53:47 +000044 PJ_LOG(1,(sender, "%s: %s [status=%d]", title, errmsg, status));
Benny Prijono268ca612006-02-07 12:34:11 +000045}
46
47
Benny Prijonoeebe9af2006-06-13 22:57:13 +000048static void init_data()
Benny Prijonodc39fe82006-05-26 12:17:46 +000049{
50 unsigned i;
51
Benny Prijonoc97608e2007-03-23 16:34:20 +000052 pj_bzero(&pjsua_var, sizeof(pjsua_var));
53
Benny Prijonoeebe9af2006-06-13 22:57:13 +000054 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i)
55 pjsua_var.acc[i].index = i;
56
57 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i)
58 pjsua_var.tpdata[i].index = i;
Benny Prijonoc97608e2007-03-23 16:34:20 +000059
60 pjsua_var.stun_status = PJ_EUNKNOWN;
Benny Prijono6ba8c542007-10-16 01:34:14 +000061 pjsua_var.nat_status = PJ_EPENDING;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000062}
Benny Prijonodc39fe82006-05-26 12:17:46 +000063
64
Benny Prijono1f61a8f2007-08-16 10:11:44 +000065PJ_DEF(void) pjsua_logging_config_default(pjsua_logging_config *cfg)
66{
67 pj_bzero(cfg, sizeof(*cfg));
68
69 cfg->msg_logging = PJ_TRUE;
70 cfg->level = 5;
71 cfg->console_level = 4;
72 cfg->decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME |
Benny Prijonod6e362a2008-07-19 17:53:47 +000073 PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE |
74 PJ_LOG_HAS_SPACE;
75#if defined(PJ_WIN32) && PJ_WIN32 != 0
76 cfg->decor |= PJ_LOG_HAS_COLOR;
77#endif
Benny Prijono1f61a8f2007-08-16 10:11:44 +000078}
79
80PJ_DEF(void) pjsua_logging_config_dup(pj_pool_t *pool,
81 pjsua_logging_config *dst,
82 const pjsua_logging_config *src)
83{
84 pj_memcpy(dst, src, sizeof(*src));
85 pj_strdup_with_null(pool, &dst->log_filename, &src->log_filename);
86}
87
88PJ_DEF(void) pjsua_config_default(pjsua_config *cfg)
89{
90 pj_bzero(cfg, sizeof(*cfg));
91
92 cfg->max_calls = 4;
93 cfg->thread_cnt = 1;
Benny Prijono91a6a172007-10-31 08:59:29 +000094 cfg->nat_type_in_sdp = 1;
Benny Prijono91d06b62008-09-20 12:16:56 +000095 cfg->force_lr = PJ_TRUE;
Benny Prijonod8179652008-01-23 20:39:07 +000096#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
97 cfg->use_srtp = PJSUA_DEFAULT_USE_SRTP;
98 cfg->srtp_secure_signaling = PJSUA_DEFAULT_SRTP_SECURE_SIGNALING;
99#endif
Benny Prijono3c5e28b2008-09-24 10:10:15 +0000100 cfg->hangup_forked_call = PJ_TRUE;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000101}
102
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000103PJ_DEF(void) pjsua_config_dup(pj_pool_t *pool,
104 pjsua_config *dst,
105 const pjsua_config *src)
106{
107 unsigned i;
108
109 pj_memcpy(dst, src, sizeof(*src));
110
111 for (i=0; i<src->outbound_proxy_cnt; ++i) {
112 pj_strdup_with_null(pool, &dst->outbound_proxy[i],
113 &src->outbound_proxy[i]);
114 }
115
116 for (i=0; i<src->cred_count; ++i) {
117 pjsip_cred_dup(pool, &dst->cred_info[i], &src->cred_info[i]);
118 }
119
120 pj_strdup_with_null(pool, &dst->user_agent, &src->user_agent);
121 pj_strdup_with_null(pool, &dst->stun_domain, &src->stun_domain);
122 pj_strdup_with_null(pool, &dst->stun_host, &src->stun_host);
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000123}
124
125PJ_DEF(void) pjsua_msg_data_init(pjsua_msg_data *msg_data)
126{
127 pj_bzero(msg_data, sizeof(*msg_data));
128 pj_list_init(&msg_data->hdr_list);
129}
130
131PJ_DEF(void) pjsua_transport_config_default(pjsua_transport_config *cfg)
132{
133 pj_bzero(cfg, sizeof(*cfg));
134 pjsip_tls_setting_default(&cfg->tls_setting);
135}
136
137PJ_DEF(void) pjsua_transport_config_dup(pj_pool_t *pool,
138 pjsua_transport_config *dst,
139 const pjsua_transport_config *src)
140{
141 PJ_UNUSED_ARG(pool);
142 pj_memcpy(dst, src, sizeof(*src));
143}
144
145PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg)
146{
147 pj_bzero(cfg, sizeof(*cfg));
148
149 cfg->reg_timeout = PJSUA_REG_INTERVAL;
150 cfg->transport_id = PJSUA_INVALID_ID;
Benny Prijonoe8554ef2008-03-22 09:33:52 +0000151 cfg->allow_contact_rewrite = PJ_TRUE;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000152 cfg->require_100rel = pjsua_var.ua_cfg.require_100rel;
Benny Prijonobddef2c2007-10-31 13:28:08 +0000153 cfg->ka_interval = 15;
154 cfg->ka_data = pj_str("\r\n");
Benny Prijonod8179652008-01-23 20:39:07 +0000155#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
156 cfg->use_srtp = pjsua_var.ua_cfg.use_srtp;
157 cfg->srtp_secure_signaling = pjsua_var.ua_cfg.srtp_secure_signaling;
158#endif
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000159}
160
161PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg)
162{
163 pj_bzero(cfg, sizeof(*cfg));
164}
165
166PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg)
167{
168 pj_bzero(cfg, sizeof(*cfg));
169
170 cfg->clock_rate = PJSUA_DEFAULT_CLOCK_RATE;
Benny Prijono50f19b32008-03-11 13:15:43 +0000171 cfg->snd_clock_rate = 0;
Benny Prijono7d60d052008-03-29 12:24:20 +0000172 cfg->channel_count = 1;
Benny Prijono37c710b2008-01-10 12:09:26 +0000173 cfg->audio_frame_ptime = PJSUA_DEFAULT_AUDIO_FRAME_PTIME;
174 cfg->max_media_ports = PJSUA_MAX_CONF_PORTS;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000175 cfg->has_ioqueue = PJ_TRUE;
176 cfg->thread_cnt = 1;
177 cfg->quality = PJSUA_DEFAULT_CODEC_QUALITY;
178 cfg->ilbc_mode = PJSUA_DEFAULT_ILBC_MODE;
179 cfg->ec_tail_len = PJSUA_DEFAULT_EC_TAIL_LEN;
180 cfg->jb_init = cfg->jb_min_pre = cfg->jb_max_pre = cfg->jb_max = -1;
Nanang Izzuddin68559c32008-06-13 17:01:46 +0000181 cfg->snd_auto_close_time = -1;
Benny Prijonof76e1392008-06-06 14:51:48 +0000182
183 cfg->turn_conn_type = PJ_TURN_TP_UDP;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000184}
185
Benny Prijonodc39fe82006-05-26 12:17:46 +0000186
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000187/*****************************************************************************
188 * This is a very simple PJSIP module, whose sole purpose is to display
189 * incoming and outgoing messages to log. This module will have priority
190 * higher than transport layer, which means:
191 *
192 * - incoming messages will come to this module first before reaching
193 * transaction layer.
194 *
195 * - outgoing messages will come to this module last, after the message
196 * has been 'printed' to contiguous buffer by transport layer and
197 * appropriate transport instance has been decided for this message.
198 *
199 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000200
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000201/* Notification on incoming messages */
202static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
203{
Benny Prijonob4a17c92006-07-10 14:40:21 +0000204 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
205 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000206 "--end msg--",
207 rdata->msg_info.len,
208 pjsip_rx_data_get_info(rdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000209 rdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000210 rdata->pkt_info.src_name,
211 rdata->pkt_info.src_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000212 (int)rdata->msg_info.len,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000213 rdata->msg_info.msg_buf));
214
215 /* Always return false, otherwise messages will not get processed! */
216 return PJ_FALSE;
217}
Benny Prijonodc39fe82006-05-26 12:17:46 +0000218
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000219/* Notification on outgoing messages */
220static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
221{
222
223 /* Important note:
224 * tp_info field is only valid after outgoing messages has passed
225 * transport layer. So don't try to access tp_info when the module
226 * has lower priority than transport layer.
227 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000228
Benny Prijonob4a17c92006-07-10 14:40:21 +0000229 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
230 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000231 "--end msg--",
232 (tdata->buf.cur - tdata->buf.start),
233 pjsip_tx_data_get_info(tdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000234 tdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000235 tdata->tp_info.dst_name,
236 tdata->tp_info.dst_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000237 (int)(tdata->buf.cur - tdata->buf.start),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000238 tdata->buf.start));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000239
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000240 /* Always return success, otherwise message will not get sent! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000241 return PJ_SUCCESS;
242}
243
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000244/* The module instance. */
245static pjsip_module pjsua_msg_logger =
246{
247 NULL, NULL, /* prev, next. */
248 { "mod-pjsua-log", 13 }, /* Name. */
249 -1, /* Id */
250 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
251 NULL, /* load() */
252 NULL, /* start() */
253 NULL, /* stop() */
254 NULL, /* unload() */
255 &logging_on_rx_msg, /* on_rx_request() */
256 &logging_on_rx_msg, /* on_rx_response() */
257 &logging_on_tx_msg, /* on_tx_request. */
258 &logging_on_tx_msg, /* on_tx_response() */
259 NULL, /* on_tsx_state() */
260
261};
262
263
264/*****************************************************************************
Benny Prijono56315612006-07-18 14:39:40 +0000265 * Another simple module to handle incoming OPTIONS request
266 */
267
268/* Notification on incoming request */
269static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
270{
271 pjsip_tx_data *tdata;
272 pjsip_response_addr res_addr;
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000273 pjmedia_transport_info tpinfo;
Benny Prijono56315612006-07-18 14:39:40 +0000274 pjmedia_sdp_session *sdp;
275 const pjsip_hdr *cap_hdr;
276 pj_status_t status;
277
278 /* Only want to handle OPTIONS requests */
279 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000280 pjsip_get_options_method()) != 0)
Benny Prijono56315612006-07-18 14:39:40 +0000281 {
282 return PJ_FALSE;
283 }
284
285 /* Create basic response. */
286 status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL,
287 &tdata);
288 if (status != PJ_SUCCESS) {
289 pjsua_perror(THIS_FILE, "Unable to create OPTIONS response", status);
290 return PJ_TRUE;
291 }
292
293 /* Add Allow header */
294 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ALLOW, 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 Accept header */
301 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, 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 Supported header */
308 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_SUPPORTED, 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 Allow-Events header from the evsub module */
315 cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL);
316 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000317 pjsip_msg_add_hdr(tdata->msg,
318 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000319 }
320
321 /* Add User-Agent header */
322 if (pjsua_var.ua_cfg.user_agent.slen) {
323 const pj_str_t USER_AGENT = { "User-Agent", 10};
324 pjsip_hdr *h;
325
326 h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
327 &USER_AGENT,
328 &pjsua_var.ua_cfg.user_agent);
329 pjsip_msg_add_hdr(tdata->msg, h);
330 }
331
Benny Prijono617c5bc2007-04-02 19:51:21 +0000332 /* Get media socket info */
Benny Prijono734fc2d2008-03-17 16:05:35 +0000333 pjmedia_transport_info_init(&tpinfo);
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000334 pjmedia_transport_get_info(pjsua_var.calls[0].med_tp, &tpinfo);
Benny Prijono617c5bc2007-04-02 19:51:21 +0000335
Benny Prijono56315612006-07-18 14:39:40 +0000336 /* Add SDP body, using call0's RTP address */
337 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool, 1,
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000338 &tpinfo.sock_info, &sdp);
Benny Prijono56315612006-07-18 14:39:40 +0000339 if (status == PJ_SUCCESS) {
340 pjsip_create_sdp_body(tdata->pool, sdp, &tdata->msg->body);
341 }
342
343 /* Send response statelessly */
344 pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
345 status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, tdata, NULL, NULL);
346 if (status != PJ_SUCCESS)
347 pjsip_tx_data_dec_ref(tdata);
348
349 return PJ_TRUE;
350}
351
352
353/* The module instance. */
354static pjsip_module pjsua_options_handler =
355{
356 NULL, NULL, /* prev, next. */
357 { "mod-pjsua-options", 17 }, /* Name. */
358 -1, /* Id */
359 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
360 NULL, /* load() */
361 NULL, /* start() */
362 NULL, /* stop() */
363 NULL, /* unload() */
364 &options_on_rx_request, /* on_rx_request() */
365 NULL, /* on_rx_response() */
366 NULL, /* on_tx_request. */
367 NULL, /* on_tx_response() */
368 NULL, /* on_tsx_state() */
369
370};
371
372
373/*****************************************************************************
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000374 * These two functions are the main callbacks registered to PJSIP stack
375 * to receive SIP request and response messages that are outside any
376 * dialogs and any transactions.
377 */
Benny Prijono268ca612006-02-07 12:34:11 +0000378
379/*
380 * Handler for receiving incoming requests.
381 *
382 * This handler serves multiple purposes:
383 * - it receives requests outside dialogs.
384 * - it receives requests inside dialogs, when the requests are
385 * unhandled by other dialog usages. Example of these
386 * requests are: MESSAGE.
387 */
388static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
389{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000390 pj_bool_t processed = PJ_FALSE;
391
392 PJSUA_LOCK();
Benny Prijono38998232006-02-08 22:44:25 +0000393
Benny Prijono84126ab2006-02-09 09:30:09 +0000394 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
Benny Prijono38998232006-02-08 22:44:25 +0000395
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000396 processed = pjsua_call_on_incoming(rdata);
Benny Prijono38998232006-02-08 22:44:25 +0000397 }
398
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000399 PJSUA_UNLOCK();
400
401 return processed;
Benny Prijono268ca612006-02-07 12:34:11 +0000402}
403
404
405/*
406 * Handler for receiving incoming responses.
407 *
408 * This handler serves multiple purposes:
409 * - it receives strayed responses (i.e. outside any dialog and
410 * outside any transactions).
411 * - it receives responses coming to a transaction, when pjsua
412 * module is set as transaction user for the transaction.
413 * - it receives responses inside a dialog, when these responses
414 * are unhandled by other dialog usages.
415 */
416static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
417{
418 PJ_UNUSED_ARG(rdata);
Benny Prijono268ca612006-02-07 12:34:11 +0000419 return PJ_FALSE;
420}
421
422
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000423/*****************************************************************************
424 * Logging.
425 */
426
427/* Log callback */
428static void log_writer(int level, const char *buffer, int len)
Benny Prijono268ca612006-02-07 12:34:11 +0000429{
Benny Prijono572d4852006-11-23 21:50:02 +0000430 /* Write to file, stdout or application callback. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000431
432 if (pjsua_var.log_file) {
433 pj_ssize_t size = len;
434 pj_file_write(pjsua_var.log_file, buffer, &size);
Benny Prijono980ca0a2007-04-03 17:55:08 +0000435 /* This will slow things down considerably! Don't do it!
436 pj_file_flush(pjsua_var.log_file);
437 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000438 }
439
Benny Prijono572d4852006-11-23 21:50:02 +0000440 if (level <= (int)pjsua_var.log_cfg.console_level) {
441 if (pjsua_var.log_cfg.cb)
442 (*pjsua_var.log_cfg.cb)(level, buffer, len);
443 else
444 pj_log_write(level, buffer, len);
445 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000446}
447
448
449/*
450 * Application can call this function at any time (after pjsua_create(), of
451 * course) to change logging settings.
452 */
453PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg)
454{
455 pj_status_t status;
456
457 /* Save config. */
458 pjsua_logging_config_dup(pjsua_var.pool, &pjsua_var.log_cfg, cfg);
459
460 /* Redirect log function to ours */
461 pj_log_set_log_func( &log_writer );
462
Benny Prijono9cb09a22007-08-30 09:35:10 +0000463 /* Set decor */
464 pj_log_set_decor(pjsua_var.log_cfg.decor);
465
Benny Prijono4190cf92008-01-18 13:25:05 +0000466 /* Set log level */
467 pj_log_set_level(pjsua_var.log_cfg.level);
468
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000469 /* Close existing file, if any */
470 if (pjsua_var.log_file) {
471 pj_file_close(pjsua_var.log_file);
472 pjsua_var.log_file = NULL;
473 }
474
475 /* If output log file is desired, create the file: */
476 if (pjsua_var.log_cfg.log_filename.slen) {
477
478 status = pj_file_open(pjsua_var.pool,
479 pjsua_var.log_cfg.log_filename.ptr,
480 PJ_O_WRONLY,
481 &pjsua_var.log_file);
482
483 if (status != PJ_SUCCESS) {
484 pjsua_perror(THIS_FILE, "Error creating log file", status);
485 return status;
486 }
487 }
488
489 /* Unregister msg logging if it's previously registered */
490 if (pjsua_msg_logger.id >= 0) {
491 pjsip_endpt_unregister_module(pjsua_var.endpt, &pjsua_msg_logger);
492 pjsua_msg_logger.id = -1;
493 }
494
495 /* Enable SIP message logging */
496 if (pjsua_var.log_cfg.msg_logging)
497 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_msg_logger);
498
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000499 return PJ_SUCCESS;
500}
501
502
503/*****************************************************************************
504 * PJSUA Base API.
505 */
506
507/* Worker thread function. */
508static int worker_thread(void *arg)
509{
510 enum { TIMEOUT = 10 };
Benny Prijonof8baa872006-02-27 23:54:23 +0000511
Benny Prijono268ca612006-02-07 12:34:11 +0000512 PJ_UNUSED_ARG(arg);
513
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000514 while (!pjsua_var.thread_quit_flag) {
515 int count;
516
517 count = pjsua_handle_events(TIMEOUT);
518 if (count < 0)
519 pj_thread_sleep(TIMEOUT);
520 }
Benny Prijono268ca612006-02-07 12:34:11 +0000521
522 return 0;
523}
524
Benny Prijonodc39fe82006-05-26 12:17:46 +0000525
Benny Prijono8389c312008-02-21 21:36:34 +0000526/* Init random seed */
527static void init_random_seed(void)
528{
529 pj_sockaddr addr;
530 const pj_str_t *hostname;
531 pj_uint32_t pid;
532 pj_time_val t;
533 unsigned seed=0;
534
535 /* Add hostname */
536 hostname = pj_gethostname();
537 seed = pj_hash_calc(seed, hostname->ptr, (int)hostname->slen);
538
539 /* Add primary IP address */
540 if (pj_gethostip(pj_AF_INET(), &addr)==PJ_SUCCESS)
541 seed = pj_hash_calc(seed, &addr.ipv4.sin_addr, 4);
542
543 /* Get timeofday */
544 pj_gettimeofday(&t);
545 seed = pj_hash_calc(seed, &t, sizeof(t));
546
547 /* Add PID */
548 pid = pj_getpid();
549 seed = pj_hash_calc(seed, &pid, sizeof(pid));
550
551 /* Init random seed */
552 pj_srand(seed);
553}
554
Benny Prijono268ca612006-02-07 12:34:11 +0000555/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000556 * Instantiate pjsua application.
Benny Prijonodc39fe82006-05-26 12:17:46 +0000557 */
558PJ_DEF(pj_status_t) pjsua_create(void)
559{
560 pj_status_t status;
Benny Prijono268ca612006-02-07 12:34:11 +0000561
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000562 /* Init pjsua data */
563 init_data();
Benny Prijono268ca612006-02-07 12:34:11 +0000564
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000565 /* Set default logging settings */
566 pjsua_logging_config_default(&pjsua_var.log_cfg);
567
568 /* Init PJLIB: */
Benny Prijono268ca612006-02-07 12:34:11 +0000569 status = pj_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000570 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
571
Benny Prijono8389c312008-02-21 21:36:34 +0000572 /* Init random seed */
573 init_random_seed();
Benny Prijono268ca612006-02-07 12:34:11 +0000574
Benny Prijonofccab712006-02-22 22:23:22 +0000575 /* Init PJLIB-UTIL: */
Benny Prijonofccab712006-02-22 22:23:22 +0000576 status = pjlib_util_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000577 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonofccab712006-02-22 22:23:22 +0000578
Benny Prijonoec921342007-03-24 13:00:30 +0000579 /* Init PJNATH */
580 status = pjnath_init();
581 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono268ca612006-02-07 12:34:11 +0000582
Benny Prijono094d3ad2006-11-21 08:41:00 +0000583 /* Set default sound device ID */
584 pjsua_var.cap_dev = pjsua_var.play_dev = -1;
585
Benny Prijono268ca612006-02-07 12:34:11 +0000586 /* Init caching pool. */
Benny Prijono106f5b72007-08-30 13:49:33 +0000587 pj_caching_pool_init(&pjsua_var.cp, NULL, 0);
Benny Prijono268ca612006-02-07 12:34:11 +0000588
589 /* Create memory pool for application. */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000590 pjsua_var.pool = pjsua_pool_create("pjsua", 1000, 1000);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000591
592 PJ_ASSERT_RETURN(pjsua_var.pool, PJ_ENOMEM);
Benny Prijono268ca612006-02-07 12:34:11 +0000593
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000594 /* Create mutex */
595 status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua",
596 &pjsua_var.mutex);
Benny Prijonoccf95622006-02-07 18:48:01 +0000597 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000598 pjsua_perror(THIS_FILE, "Unable to create mutex", status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000599 return status;
600 }
601
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000602 /* Must create SIP endpoint to initialize SIP parser. The parser
603 * is needed for example when application needs to call pjsua_verify_url().
Benny Prijonob8c25182006-04-29 08:31:09 +0000604 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000605 status = pjsip_endpt_create(&pjsua_var.cp.factory,
606 pj_gethostname()->ptr,
607 &pjsua_var.endpt);
608 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono39879152006-02-23 02:09:10 +0000609
610
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000611 return PJ_SUCCESS;
612}
613
614
615/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000616 * Initialize pjsua with the specified settings. All the settings are
617 * optional, and the default values will be used when the config is not
618 * specified.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000619 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000620PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
621 const pjsua_logging_config *log_cfg,
622 const pjsua_media_config *media_cfg)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000623{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000624 pjsua_config default_cfg;
625 pjsua_media_config default_media_cfg;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000626 const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
Benny Prijono3c5e28b2008-09-24 10:10:15 +0000627 pjsip_ua_init_param ua_init_param;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000628 pj_status_t status;
629
630
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000631 /* Create default configurations when the config is not supplied */
632
633 if (ua_cfg == NULL) {
634 pjsua_config_default(&default_cfg);
635 ua_cfg = &default_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000636 }
637
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000638 if (media_cfg == NULL) {
639 pjsua_media_config_default(&default_media_cfg);
640 media_cfg = &default_media_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000641 }
642
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000643 /* Initialize logging first so that info/errors can be captured */
644 if (log_cfg) {
645 status = pjsua_reconfigure_logging(log_cfg);
646 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000647 }
648
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000649 /* If nameserver is configured, create DNS resolver instance and
650 * set it to be used by SIP resolver.
651 */
652 if (ua_cfg->nameserver_count) {
653#if PJSIP_HAS_RESOLVER
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000654 unsigned i;
655
656 /* Create DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000657 status = pjsip_endpt_create_resolver(pjsua_var.endpt,
658 &pjsua_var.resolver);
Benny Prijono318f3842007-05-15 20:27:08 +0000659 if (status != PJ_SUCCESS) {
660 pjsua_perror(THIS_FILE, "Error creating resolver", status);
661 return status;
662 }
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000663
664 /* Configure nameserver for the DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000665 status = pj_dns_resolver_set_ns(pjsua_var.resolver,
666 ua_cfg->nameserver_count,
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000667 ua_cfg->nameserver, NULL);
668 if (status != PJ_SUCCESS) {
669 pjsua_perror(THIS_FILE, "Error setting nameserver", status);
670 return status;
671 }
672
673 /* Set this DNS resolver to be used by the SIP resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000674 status = pjsip_endpt_set_resolver(pjsua_var.endpt, pjsua_var.resolver);
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000675 if (status != PJ_SUCCESS) {
676 pjsua_perror(THIS_FILE, "Error setting DNS resolver", status);
677 return status;
678 }
679
680 /* Print nameservers */
681 for (i=0; i<ua_cfg->nameserver_count; ++i) {
682 PJ_LOG(4,(THIS_FILE, "Nameserver %.*s added",
683 (int)ua_cfg->nameserver[i].slen,
684 ua_cfg->nameserver[i].ptr));
685 }
686#else
687 PJ_LOG(2,(THIS_FILE,
688 "DNS resolver is disabled (PJSIP_HAS_RESOLVER==0)"));
689#endif
690 }
691
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000692 /* Init SIP UA: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000693
694 /* Initialize transaction layer: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000695 status = pjsip_tsx_layer_init_module(pjsua_var.endpt);
696 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000697
Benny Prijonodc39fe82006-05-26 12:17:46 +0000698
699 /* Initialize UA layer module: */
Benny Prijono3c5e28b2008-09-24 10:10:15 +0000700 pj_bzero(&ua_init_param, sizeof(ua_init_param));
701 if (ua_cfg->hangup_forked_call) {
702 ua_init_param.on_dlg_forked = &on_dlg_forked;
703 }
704 status = pjsip_ua_init_module( pjsua_var.endpt, &ua_init_param);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000705 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000706
Benny Prijonodc39fe82006-05-26 12:17:46 +0000707
Benny Prijono053f5222006-11-11 16:16:04 +0000708 /* Initialize Replaces support. */
709 status = pjsip_replaces_init_module( pjsua_var.endpt );
710 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
711
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000712 /* Initialize 100rel support */
713 status = pjsip_100rel_init_module(pjsua_var.endpt);
714 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono053f5222006-11-11 16:16:04 +0000715
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000716 /* Initialize and register PJSUA application module. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000717 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000718 const pjsip_module mod_initializer =
Benny Prijonodc39fe82006-05-26 12:17:46 +0000719 {
720 NULL, NULL, /* prev, next. */
721 { "mod-pjsua", 9 }, /* Name. */
722 -1, /* Id */
723 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
724 NULL, /* load() */
725 NULL, /* start() */
726 NULL, /* stop() */
727 NULL, /* unload() */
728 &mod_pjsua_on_rx_request, /* on_rx_request() */
729 &mod_pjsua_on_rx_response, /* on_rx_response() */
730 NULL, /* on_tx_request. */
731 NULL, /* on_tx_response() */
732 NULL, /* on_tsx_state() */
733 };
734
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000735 pjsua_var.mod = mod_initializer;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000736
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000737 status = pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_var.mod);
738 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000739 }
740
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000741
Benny Prijonodc39fe82006-05-26 12:17:46 +0000742
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000743 /* Initialize PJSUA call subsystem: */
744 status = pjsua_call_subsys_init(ua_cfg);
745 if (status != PJ_SUCCESS)
Benny Prijonodc39fe82006-05-26 12:17:46 +0000746 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000747
748
Benny Prijonoc97608e2007-03-23 16:34:20 +0000749 /* Start resolving STUN server */
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000750
Benny Prijonoc97608e2007-03-23 16:34:20 +0000751 status = pjsua_resolve_stun_server(PJ_FALSE);
752 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
753 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
754 return status;
755 }
756
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000757 /* Initialize PJSUA media subsystem */
758 status = pjsua_media_subsys_init(media_cfg);
759 if (status != PJ_SUCCESS)
760 goto on_error;
761
Benny Prijonodc39fe82006-05-26 12:17:46 +0000762
763 /* Init core SIMPLE module : */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000764 status = pjsip_evsub_init_module(pjsua_var.endpt);
765 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000766
Benny Prijonodc39fe82006-05-26 12:17:46 +0000767
768 /* Init presence module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000769 status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance());
770 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000771
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000772 /* Init PUBLISH module */
773 pjsip_publishc_init_module(pjsua_var.endpt);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000774
775 /* Init xfer/REFER module */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000776 status = pjsip_xfer_init_module( pjsua_var.endpt );
777 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000778
779 /* Init pjsua presence handler: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000780 status = pjsua_pres_init();
781 if (status != PJ_SUCCESS)
782 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000783
784 /* Init out-of-dialog MESSAGE request handler. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000785 status = pjsua_im_init();
786 if (status != PJ_SUCCESS)
787 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000788
Benny Prijonoc8141a82006-08-20 09:12:19 +0000789 /* Register OPTIONS handler */
790 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_options_handler);
791
792 /* Add OPTIONS in Allow header */
793 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_ALLOW,
794 NULL, 1, &STR_OPTIONS);
795
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000796 /* Start worker thread if needed. */
797 if (pjsua_var.ua_cfg.thread_cnt) {
798 unsigned i;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000799
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000800 if (pjsua_var.ua_cfg.thread_cnt > PJ_ARRAY_SIZE(pjsua_var.thread))
801 pjsua_var.ua_cfg.thread_cnt = PJ_ARRAY_SIZE(pjsua_var.thread);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000802
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000803 for (i=0; i<pjsua_var.ua_cfg.thread_cnt; ++i) {
804 status = pj_thread_create(pjsua_var.pool, "pjsua", &worker_thread,
805 NULL, 0, 0, &pjsua_var.thread[i]);
806 if (status != PJ_SUCCESS)
807 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000808 }
Benny Prijonof521eb02006-08-06 23:07:25 +0000809 PJ_LOG(4,(THIS_FILE, "%d SIP worker threads created",
810 pjsua_var.ua_cfg.thread_cnt));
811 } else {
812 PJ_LOG(4,(THIS_FILE, "No SIP worker threads created"));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000813 }
814
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000815 /* Done! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000816
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000817 PJ_LOG(3,(THIS_FILE, "pjsua version %s for %s initialized",
Benny Prijono106f5b72007-08-30 13:49:33 +0000818 pj_get_version(), PJ_OS_NAME));
Benny Prijonoccf95622006-02-07 18:48:01 +0000819
Benny Prijono268ca612006-02-07 12:34:11 +0000820 return PJ_SUCCESS;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000821
822on_error:
823 pjsua_destroy();
824 return status;
Benny Prijono268ca612006-02-07 12:34:11 +0000825}
826
827
Benny Prijono834aee32006-02-19 01:38:06 +0000828/* Sleep with polling */
829static void busy_sleep(unsigned msec)
830{
831 pj_time_val timeout, now;
832
833 pj_gettimeofday(&timeout);
834 timeout.msec += msec;
835 pj_time_val_normalize(&timeout);
836
837 do {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000838 while (pjsua_handle_events(10) > 0)
839 ;
Benny Prijono834aee32006-02-19 01:38:06 +0000840 pj_gettimeofday(&now);
841 } while (PJ_TIME_VAL_LT(now, timeout));
842}
843
Benny Prijonoebbf6892007-03-24 17:37:25 +0000844
Benny Prijonoebbf6892007-03-24 17:37:25 +0000845/*
846 * Callback function to receive notification from the resolver
847 * when the resolution process completes.
848 */
849static void stun_dns_srv_resolver_cb(void *user_data,
850 pj_status_t status,
851 const pj_dns_srv_record *rec)
852{
853 unsigned i;
854
855 PJ_UNUSED_ARG(user_data);
856
857 pjsua_var.stun_status = status;
858
859 if (status != PJ_SUCCESS) {
860 /* DNS SRV resolution failed. If stun_host is specified, resolve
861 * it with gethostbyname()
862 */
863 if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000864 pj_str_t str_host, str_port;
865 int port;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000866 pj_hostent he;
867
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000868 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
869 if (str_port.ptr != NULL) {
870 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
871 str_host.slen = (str_port.ptr - str_host.ptr);
872 str_port.ptr++;
873 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
874 str_host.slen - 1;
875 port = (int)pj_strtoul(&str_port);
876 if (port < 1 || port > 65535) {
877 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
878 pjsua_var.stun_status = PJ_EINVAL;
879 return;
880 }
881 } else {
882 str_host = pjsua_var.ua_cfg.stun_host;
883 port = 3478;
884 }
885
886 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000887
888 if (pjsua_var.stun_status == PJ_SUCCESS) {
889 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
890 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000891 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000892
893 PJ_LOG(3,(THIS_FILE,
894 "STUN server %.*s resolved, address is %s:%d",
895 (int)pjsua_var.ua_cfg.stun_host.slen,
896 pjsua_var.ua_cfg.stun_host.ptr,
897 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
898 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
899 }
900 } else {
901 char errmsg[PJ_ERR_MSG_SIZE];
902
903 pj_strerror(status, errmsg, sizeof(errmsg));
904 PJ_LOG(1,(THIS_FILE,
905 "DNS SRV resolution failed for STUN server %.*s: %s",
906 (int)pjsua_var.ua_cfg.stun_domain.slen,
907 pjsua_var.ua_cfg.stun_domain.ptr,
908 errmsg));
909 }
910 return;
911 }
912
Benny Prijonof5afa922007-06-11 16:51:18 +0000913 pj_assert(rec->count != 0 && rec->entry[0].server.addr_count != 0);
914 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL,
915 rec->entry[0].port);
916 pjsua_var.stun_srv.ipv4.sin_addr.s_addr =
917 rec->entry[0].server.addr[0].s_addr;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000918
919 PJ_LOG(3,(THIS_FILE, "_stun._udp.%.*s resolved, found %d entry(s):",
920 (int)pjsua_var.ua_cfg.stun_domain.slen,
921 pjsua_var.ua_cfg.stun_domain.ptr,
922 rec->count));
923
924 for (i=0; i<rec->count; ++i) {
925 PJ_LOG(3,(THIS_FILE,
926 " %d: prio=%d, weight=%d %s:%d",
927 i, rec->entry[i].priority, rec->entry[i].weight,
Benny Prijonof5afa922007-06-11 16:51:18 +0000928 pj_inet_ntoa(rec->entry[i].server.addr[0]),
929 (int)rec->entry[i].port));
Benny Prijonoebbf6892007-03-24 17:37:25 +0000930 }
931
932}
933
Benny Prijono268ca612006-02-07 12:34:11 +0000934/*
Benny Prijonoc97608e2007-03-23 16:34:20 +0000935 * Resolve STUN server.
936 */
937pj_status_t pjsua_resolve_stun_server(pj_bool_t wait)
938{
939 if (pjsua_var.stun_status == PJ_EUNKNOWN) {
940 /* Initialize STUN configuration */
941 pj_stun_config_init(&pjsua_var.stun_cfg, &pjsua_var.cp.factory, 0,
942 pjsip_endpt_get_ioqueue(pjsua_var.endpt),
943 pjsip_endpt_get_timer_heap(pjsua_var.endpt));
944
945 /* Start STUN server resolution */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000946
947 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000948
Benny Prijonoebbf6892007-03-24 17:37:25 +0000949 /* If stun_domain is specified, resolve STUN servers with DNS
950 * SRV resolution.
951 */
952 if (pjsua_var.ua_cfg.stun_domain.slen) {
953 pj_str_t res_type;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000954 pj_status_t status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000955
956 /* Fail if resolver is not configured */
957 if (pjsua_var.resolver == NULL) {
958 PJ_LOG(1,(THIS_FILE, "Nameserver must be configured when "
959 "stun_domain is specified"));
960 pjsua_var.stun_status = PJLIB_UTIL_EDNSNONS;
961 return PJLIB_UTIL_EDNSNONS;
962 }
963 res_type = pj_str("_stun._udp");
Benny Prijonoda9785b2007-04-02 20:43:06 +0000964 status =
Benny Prijonoebbf6892007-03-24 17:37:25 +0000965 pj_dns_srv_resolve(&pjsua_var.ua_cfg.stun_domain, &res_type,
966 3478, pjsua_var.pool, pjsua_var.resolver,
Benny Prijonof5afa922007-06-11 16:51:18 +0000967 0, NULL, &stun_dns_srv_resolver_cb, NULL);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000968 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +0000969 pjsua_perror(THIS_FILE, "Error starting DNS SRV resolution",
970 pjsua_var.stun_status);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000971 pjsua_var.stun_status = status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000972 return pjsua_var.stun_status;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000973 } else {
974 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000975 }
976 }
977 /* Otherwise if stun_host is specified, resolve STUN server with
978 * gethostbyname().
979 */
980 else if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000981 pj_str_t str_host, str_port;
982 int port;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000983 pj_hostent he;
984
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000985 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
986 if (str_port.ptr != NULL) {
987 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
988 str_host.slen = (str_port.ptr - str_host.ptr);
989 str_port.ptr++;
990 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
991 str_host.slen - 1;
992 port = (int)pj_strtoul(&str_port);
993 if (port < 1 || port > 65535) {
994 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
995 pjsua_var.stun_status = PJ_EINVAL;
996 return pjsua_var.stun_status;
997 }
998 } else {
999 str_host = pjsua_var.ua_cfg.stun_host;
1000 port = 3478;
1001 }
1002
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001003 pjsua_var.stun_status =
1004 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, &str_host,
1005 (pj_uint16_t)port);
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001006
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001007 if (pjsua_var.stun_status != PJ_SUCCESS) {
1008 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001009
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001010 if (pjsua_var.stun_status == PJ_SUCCESS) {
1011 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
1012 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
1013 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
1014 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001015 }
Benny Prijonoebbf6892007-03-24 17:37:25 +00001016
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001017 PJ_LOG(3,(THIS_FILE,
1018 "STUN server %.*s resolved, address is %s:%d",
1019 (int)pjsua_var.ua_cfg.stun_host.slen,
1020 pjsua_var.ua_cfg.stun_host.ptr,
1021 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
1022 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
1023
Benny Prijonoc97608e2007-03-23 16:34:20 +00001024 }
Benny Prijonoebbf6892007-03-24 17:37:25 +00001025 /* Otherwise disable STUN. */
1026 else {
1027 pjsua_var.stun_status = PJ_SUCCESS;
1028 }
1029
1030
Benny Prijonoc97608e2007-03-23 16:34:20 +00001031 return pjsua_var.stun_status;
1032
1033 } else if (pjsua_var.stun_status == PJ_EPENDING) {
1034 /* STUN server resolution has been started, wait for the
1035 * result.
1036 */
Benny Prijonoebbf6892007-03-24 17:37:25 +00001037 if (wait) {
1038 while (pjsua_var.stun_status == PJ_EPENDING)
1039 pjsua_handle_events(10);
1040 }
1041
1042 return pjsua_var.stun_status;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001043
1044 } else {
1045 /* STUN server has been resolved, return the status */
1046 return pjsua_var.stun_status;
1047 }
1048}
1049
1050/*
Benny Prijono268ca612006-02-07 12:34:11 +00001051 * Destroy pjsua.
1052 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001053PJ_DEF(pj_status_t) pjsua_destroy(void)
Benny Prijono268ca612006-02-07 12:34:11 +00001054{
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001055 int i; /* Must be signed */
Benny Prijono268ca612006-02-07 12:34:11 +00001056
1057 /* Signal threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001058 pjsua_var.thread_quit_flag = 1;
Benny Prijono268ca612006-02-07 12:34:11 +00001059
1060 /* Wait worker threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001061 for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) {
1062 if (pjsua_var.thread[i]) {
1063 pj_thread_join(pjsua_var.thread[i]);
1064 pj_thread_destroy(pjsua_var.thread[i]);
1065 pjsua_var.thread[i] = NULL;
Benny Prijonoe4f2abb2006-02-10 14:04:05 +00001066 }
Benny Prijono268ca612006-02-07 12:34:11 +00001067 }
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001068
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001069 if (pjsua_var.endpt) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001070 /* Terminate all calls. */
1071 pjsua_call_hangup_all();
1072
Benny Prijono7f6ee022008-07-31 08:32:46 +00001073 /* Set all accounts to offline */
1074 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1075 if (!pjsua_var.acc[i].valid)
1076 continue;
1077 pjsua_var.acc[i].online_status = PJ_FALSE;
1078 pj_bzero(&pjsua_var.acc[i].rpid, sizeof(pjrpid_element));
1079 }
1080
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001081 /* Terminate all presence subscriptions. */
1082 pjsua_pres_shutdown();
1083
Benny Prijono4d1cc7b2008-07-14 09:55:01 +00001084 /* Unregister all accounts */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001085 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1086 if (!pjsua_var.acc[i].valid)
1087 continue;
1088
1089 if (pjsua_var.acc[i].regc) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001090 pjsua_acc_set_registration(i, PJ_FALSE);
1091 }
1092 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001093 }
Benny Prijono834aee32006-02-19 01:38:06 +00001094
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001095 /* Destroy media */
1096 pjsua_media_subsys_destroy();
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001097
Benny Prijono268ca612006-02-07 12:34:11 +00001098 /* Destroy endpoint. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001099 if (pjsua_var.endpt) {
Benny Prijonoff3b1462008-07-14 09:32:14 +00001100 /* Wait for some time to allow unregistration and ICE/TURN
1101 * transports shutdown to complete:
1102 */
1103 PJ_LOG(4,(THIS_FILE, "Shutting down..."));
1104 busy_sleep(1000);
1105
Benny Prijono4d1cc7b2008-07-14 09:55:01 +00001106 PJ_LOG(4,(THIS_FILE, "Destroying..."));
1107
Benny Prijonod7e26582008-07-18 23:51:49 +00001108 /* Must destroy endpoint first before destroying pools in
1109 * buddies or accounts, since shutting down transaction layer
1110 * may emit events which trigger some buddy or account callbacks
1111 * to be called.
1112 */
1113 pjsip_endpt_destroy(pjsua_var.endpt);
1114 pjsua_var.endpt = NULL;
1115
Benny Prijono4d1cc7b2008-07-14 09:55:01 +00001116 /* Destroy pool in the buddy object */
1117 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1118 if (pjsua_var.buddy[i].pool) {
1119 pj_pool_release(pjsua_var.buddy[i].pool);
1120 pjsua_var.buddy[i].pool = NULL;
1121 }
1122 }
1123
1124 /* Destroy accounts */
1125 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1126 if (pjsua_var.acc[i].pool) {
1127 pj_pool_release(pjsua_var.acc[i].pool);
1128 pjsua_var.acc[i].pool = NULL;
1129 }
1130 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001131 }
Benny Prijono268ca612006-02-07 12:34:11 +00001132
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001133 /* Destroy mutex */
1134 if (pjsua_var.mutex) {
1135 pj_mutex_destroy(pjsua_var.mutex);
1136 pjsua_var.mutex = NULL;
1137 }
1138
1139 /* Destroy pool and pool factory. */
1140 if (pjsua_var.pool) {
1141 pj_pool_release(pjsua_var.pool);
1142 pjsua_var.pool = NULL;
1143 pj_caching_pool_destroy(&pjsua_var.cp);
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00001144
1145 PJ_LOG(4,(THIS_FILE, "PJSUA destroyed..."));
1146
1147 /* End logging */
1148 if (pjsua_var.log_file) {
1149 pj_file_close(pjsua_var.log_file);
1150 pjsua_var.log_file = NULL;
1151 }
1152
1153 /* Shutdown PJLIB */
1154 pj_shutdown();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001155 }
Benny Prijono268ca612006-02-07 12:34:11 +00001156
Benny Prijonof762ee72006-12-01 11:14:37 +00001157 /* Clear pjsua_var */
1158 pj_bzero(&pjsua_var, sizeof(pjsua_var));
1159
Benny Prijono268ca612006-02-07 12:34:11 +00001160 /* Done. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001161 return PJ_SUCCESS;
1162}
1163
1164
1165/**
1166 * Application is recommended to call this function after all initialization
1167 * is done, so that the library can do additional checking set up
1168 * additional
1169 *
1170 * @return PJ_SUCCESS on success, or the appropriate error code.
1171 */
1172PJ_DEF(pj_status_t) pjsua_start(void)
1173{
1174 pj_status_t status;
1175
1176 status = pjsua_call_subsys_start();
1177 if (status != PJ_SUCCESS)
1178 return status;
1179
1180 status = pjsua_media_subsys_start();
1181 if (status != PJ_SUCCESS)
1182 return status;
1183
1184 status = pjsua_pres_start();
1185 if (status != PJ_SUCCESS)
1186 return status;
Benny Prijono268ca612006-02-07 12:34:11 +00001187
1188 return PJ_SUCCESS;
1189}
1190
Benny Prijono9fc735d2006-05-28 14:58:12 +00001191
1192/**
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001193 * Poll pjsua for events, and if necessary block the caller thread for
1194 * the specified maximum interval (in miliseconds).
1195 */
1196PJ_DEF(int) pjsua_handle_events(unsigned msec_timeout)
1197{
Benny Prijono897f9f82007-05-03 19:56:21 +00001198#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
Nanang Izzuddin82f7a412008-12-17 11:36:22 +00001199
1200 return pj_symbianos_poll(-1, msec_timeout);
1201
Benny Prijono897f9f82007-05-03 19:56:21 +00001202#else
1203
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001204 unsigned count = 0;
1205 pj_time_val tv;
1206 pj_status_t status;
1207
1208 tv.sec = 0;
1209 tv.msec = msec_timeout;
1210 pj_time_val_normalize(&tv);
1211
1212 status = pjsip_endpt_handle_events2(pjsua_var.endpt, &tv, &count);
1213
1214 if (status != PJ_SUCCESS)
1215 return -status;
1216
1217 return count;
Nanang Izzuddin82f7a412008-12-17 11:36:22 +00001218
Benny Prijono897f9f82007-05-03 19:56:21 +00001219#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001220}
1221
1222
1223/*
1224 * Create memory pool.
1225 */
1226PJ_DEF(pj_pool_t*) pjsua_pool_create( const char *name, pj_size_t init_size,
1227 pj_size_t increment)
1228{
1229 /* Pool factory is thread safe, no need to lock */
1230 return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment,
1231 NULL);
1232}
1233
1234
1235/*
1236 * Internal function to get SIP endpoint instance of pjsua, which is
1237 * needed for example to register module, create transports, etc.
1238 * Probably is only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001239 */
1240PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void)
1241{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001242 return pjsua_var.endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001243}
1244
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001245/*
1246 * Internal function to get media endpoint instance.
1247 * Only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001248 */
1249PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void)
1250{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001251 return pjsua_var.med_endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001252}
1253
Benny Prijono97b87172006-08-24 14:25:14 +00001254/*
1255 * Internal function to get PJSUA pool factory.
1256 */
1257PJ_DEF(pj_pool_factory*) pjsua_get_pool_factory(void)
1258{
1259 return &pjsua_var.cp.factory;
1260}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001261
1262/*****************************************************************************
1263 * PJSUA SIP Transport API.
1264 */
1265
1266/*
Benny Prijono23674a32007-12-01 08:59:25 +00001267 * Tools to get address string.
1268 */
1269static const char *addr_string(const pj_sockaddr_t *addr)
1270{
1271 static char str[128];
1272 str[0] = '\0';
1273 pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family,
1274 pj_sockaddr_get_addr(addr),
1275 str, sizeof(str));
1276 return str;
1277}
1278
1279/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001280 * Create and initialize SIP socket (and possibly resolve public
1281 * address via STUN, depending on config).
1282 */
Benny Prijono23674a32007-12-01 08:59:25 +00001283static pj_status_t create_sip_udp_sock(int af,
1284 const pj_str_t *bind_param,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001285 int port,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001286 pj_sock_t *p_sock,
Benny Prijono23674a32007-12-01 08:59:25 +00001287 pj_sockaddr *p_pub_addr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001288{
Benny Prijono23674a32007-12-01 08:59:25 +00001289 char stun_ip_addr[PJ_INET6_ADDRSTRLEN];
Benny Prijonoc97608e2007-03-23 16:34:20 +00001290 pj_str_t stun_srv;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001291 pj_sock_t sock;
Benny Prijono23674a32007-12-01 08:59:25 +00001292 pj_sockaddr bind_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001293 pj_status_t status;
1294
Benny Prijonoc97608e2007-03-23 16:34:20 +00001295 /* Make sure STUN server resolution has completed */
1296 status = pjsua_resolve_stun_server(PJ_TRUE);
1297 if (status != PJ_SUCCESS) {
1298 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
1299 return status;
1300 }
1301
Benny Prijono23674a32007-12-01 08:59:25 +00001302 /* Initialize bound address */
1303 if (bind_param->slen) {
1304 status = pj_sockaddr_init(af, &bind_addr, bind_param,
1305 (pj_uint16_t)port);
1306 if (status != PJ_SUCCESS) {
1307 pjsua_perror(THIS_FILE,
1308 "Unable to resolve transport bound address",
1309 status);
1310 return status;
1311 }
1312 } else {
1313 pj_sockaddr_init(af, &bind_addr, NULL, (pj_uint16_t)port);
1314 }
1315
1316 status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001317 if (status != PJ_SUCCESS) {
1318 pjsua_perror(THIS_FILE, "socket() error", status);
Benny Prijonod8410532006-06-15 11:04:33 +00001319 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001320 }
1321
Benny Prijono5f55a1f2007-12-05 05:08:29 +00001322 status = pj_sock_bind(sock, &bind_addr, pj_sockaddr_get_len(&bind_addr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001323 if (status != PJ_SUCCESS) {
1324 pjsua_perror(THIS_FILE, "bind() error", status);
1325 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001326 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001327 }
1328
Benny Prijonoe347cb02007-02-14 14:36:13 +00001329 /* If port is zero, get the bound port */
1330 if (port == 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001331 pj_sockaddr bound_addr;
Benny Prijonoe347cb02007-02-14 14:36:13 +00001332 int namelen = sizeof(bound_addr);
1333 status = pj_sock_getsockname(sock, &bound_addr, &namelen);
1334 if (status != PJ_SUCCESS) {
1335 pjsua_perror(THIS_FILE, "getsockname() error", status);
1336 pj_sock_close(sock);
1337 return status;
1338 }
1339
Benny Prijono23674a32007-12-01 08:59:25 +00001340 port = pj_sockaddr_get_port(&bound_addr);
Benny Prijonoe347cb02007-02-14 14:36:13 +00001341 }
1342
Benny Prijonoc97608e2007-03-23 16:34:20 +00001343 if (pjsua_var.stun_srv.addr.sa_family != 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001344 pj_ansi_strcpy(stun_ip_addr,pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
1345 stun_srv = pj_str(stun_ip_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001346 } else {
Benny Prijonoc97608e2007-03-23 16:34:20 +00001347 stun_srv.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001348 }
1349
1350 /* Get the published address, either by STUN or by resolving
1351 * the name of local host.
1352 */
Benny Prijono23674a32007-12-01 08:59:25 +00001353 if (pj_sockaddr_has_addr(p_pub_addr)) {
Benny Prijono744aca02007-11-11 03:06:07 +00001354 /*
1355 * Public address is already specified, no need to resolve the
1356 * address, only set the port.
1357 */
Benny Prijono23674a32007-12-01 08:59:25 +00001358 if (pj_sockaddr_get_port(p_pub_addr) == 0)
1359 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijono744aca02007-11-11 03:06:07 +00001360
1361 } else if (stun_srv.slen) {
Benny Prijono0a5cad82006-09-26 13:21:02 +00001362 /*
1363 * STUN is specified, resolve the address with STUN.
1364 */
Benny Prijono23674a32007-12-01 08:59:25 +00001365 if (af != pj_AF_INET()) {
1366 pjsua_perror(THIS_FILE, "Cannot use STUN", PJ_EAFNOTSUP);
1367 pj_sock_close(sock);
1368 return PJ_EAFNOTSUP;
1369 }
1370
Benny Prijono14c2b862007-02-21 00:40:05 +00001371 status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001372 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
1373 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
Benny Prijono23674a32007-12-01 08:59:25 +00001374 &p_pub_addr->ipv4);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001375 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +00001376 pjsua_perror(THIS_FILE, "Error contacting STUN server", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001377 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001378 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001379 }
1380
1381 } else {
Benny Prijono23674a32007-12-01 08:59:25 +00001382 pj_bzero(p_pub_addr, sizeof(pj_sockaddr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001383
Benny Prijono42d08d22007-12-20 11:23:07 +00001384 if (pj_sockaddr_has_addr(&bind_addr)) {
1385 pj_sockaddr_copy_addr(p_pub_addr, &bind_addr);
1386 } else {
1387 status = pj_gethostip(af, p_pub_addr);
1388 if (status != PJ_SUCCESS) {
1389 pjsua_perror(THIS_FILE, "Unable to get local host IP", status);
1390 pj_sock_close(sock);
1391 return status;
1392 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001393 }
1394
Benny Prijono23674a32007-12-01 08:59:25 +00001395 p_pub_addr->addr.sa_family = (pj_uint16_t)af;
1396 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001397 }
1398
1399 *p_sock = sock;
1400
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001401 PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
Benny Prijono23674a32007-12-01 08:59:25 +00001402 addr_string(p_pub_addr),
1403 (int)pj_sockaddr_get_port(p_pub_addr)));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001404
Benny Prijonod8410532006-06-15 11:04:33 +00001405 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001406}
1407
1408
1409/*
1410 * Create SIP transport.
1411 */
1412PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
1413 const pjsua_transport_config *cfg,
1414 pjsua_transport_id *p_id)
1415{
1416 pjsip_transport *tp;
1417 unsigned id;
1418 pj_status_t status;
1419
1420 PJSUA_LOCK();
1421
1422 /* Find empty transport slot */
1423 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001424 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001425 break;
1426 }
1427
1428 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1429 status = PJ_ETOOMANY;
1430 pjsua_perror(THIS_FILE, "Error creating transport", status);
1431 goto on_return;
1432 }
1433
1434 /* Create the transport */
Benny Prijonob2477142007-12-05 04:09:59 +00001435 if (type==PJSIP_TRANSPORT_UDP || type==PJSIP_TRANSPORT_UDP6) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001436 /*
Benny Prijono23674a32007-12-01 08:59:25 +00001437 * Create UDP transport (IPv4 or IPv6).
Benny Prijonoe93e2872006-06-28 16:46:49 +00001438 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001439 pjsua_transport_config config;
Benny Prijono23674a32007-12-01 08:59:25 +00001440 char hostbuf[PJ_INET6_ADDRSTRLEN];
Benny Prijonod8410532006-06-15 11:04:33 +00001441 pj_sock_t sock = PJ_INVALID_SOCKET;
Benny Prijono23674a32007-12-01 08:59:25 +00001442 pj_sockaddr pub_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001443 pjsip_host_port addr_name;
1444
1445 /* Supply default config if it's not specified */
1446 if (cfg == NULL) {
1447 pjsua_transport_config_default(&config);
1448 cfg = &config;
1449 }
1450
Benny Prijono0a5cad82006-09-26 13:21:02 +00001451 /* Initialize the public address from the config, if any */
Benny Prijono23674a32007-12-01 08:59:25 +00001452 pj_sockaddr_init(pjsip_transport_type_get_af(type), &pub_addr,
1453 NULL, (pj_uint16_t)cfg->port);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001454 if (cfg->public_addr.slen) {
Benny Prijono23674a32007-12-01 08:59:25 +00001455 status = pj_sockaddr_set_str_addr(pjsip_transport_type_get_af(type),
1456 &pub_addr, &cfg->public_addr);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001457 if (status != PJ_SUCCESS) {
1458 pjsua_perror(THIS_FILE,
1459 "Unable to resolve transport public address",
1460 status);
1461 goto on_return;
1462 }
1463 }
1464
1465 /* Create the socket and possibly resolve the address with STUN
1466 * (only when public address is not specified).
1467 */
Benny Prijono23674a32007-12-01 08:59:25 +00001468 status = create_sip_udp_sock(pjsip_transport_type_get_af(type),
1469 &cfg->bound_addr, cfg->port,
Benny Prijono0a5cad82006-09-26 13:21:02 +00001470 &sock, &pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001471 if (status != PJ_SUCCESS)
1472 goto on_return;
1473
Benny Prijono23674a32007-12-01 08:59:25 +00001474 pj_ansi_strcpy(hostbuf, addr_string(&pub_addr));
1475 addr_name.host = pj_str(hostbuf);
1476 addr_name.port = pj_sockaddr_get_port(&pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001477
1478 /* Create UDP transport */
Benny Prijono23674a32007-12-01 08:59:25 +00001479 status = pjsip_udp_transport_attach2(pjsua_var.endpt, type, sock,
1480 &addr_name, 1, &tp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001481 if (status != PJ_SUCCESS) {
1482 pjsua_perror(THIS_FILE, "Error creating SIP UDP transport",
1483 status);
1484 pj_sock_close(sock);
1485 goto on_return;
1486 }
1487
Benny Prijonoe93e2872006-06-28 16:46:49 +00001488
1489 /* Save the transport */
1490 pjsua_var.tpdata[id].type = type;
1491 pjsua_var.tpdata[id].local_name = tp->local_name;
1492 pjsua_var.tpdata[id].data.tp = tp;
1493
Benny Prijono3569c0d2007-04-06 10:29:20 +00001494#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
1495
Benny Prijonob2477142007-12-05 04:09:59 +00001496 } else if (type == PJSIP_TRANSPORT_TCP || type == PJSIP_TRANSPORT_TCP6) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001497 /*
1498 * Create TCP transport.
1499 */
1500 pjsua_transport_config config;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001501 pjsip_host_port a_name;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001502 pjsip_tpfactory *tcp;
1503 pj_sockaddr_in local_addr;
1504
1505 /* Supply default config if it's not specified */
1506 if (cfg == NULL) {
1507 pjsua_transport_config_default(&config);
1508 cfg = &config;
1509 }
1510
1511 /* Init local address */
1512 pj_sockaddr_in_init(&local_addr, 0, 0);
1513
1514 if (cfg->port)
1515 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1516
Benny Prijono0a5cad82006-09-26 13:21:02 +00001517 if (cfg->bound_addr.slen) {
1518 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1519 if (status != PJ_SUCCESS) {
1520 pjsua_perror(THIS_FILE,
1521 "Unable to resolve transport bound address",
1522 status);
1523 goto on_return;
1524 }
1525 }
1526
1527 /* Init published name */
1528 pj_bzero(&a_name, sizeof(pjsip_host_port));
1529 if (cfg->public_addr.slen)
1530 a_name.host = cfg->public_addr;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001531
1532 /* Create the TCP transport */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001533 status = pjsip_tcp_transport_start2(pjsua_var.endpt, &local_addr,
1534 &a_name, 1, &tcp);
Benny Prijonoe93e2872006-06-28 16:46:49 +00001535
1536 if (status != PJ_SUCCESS) {
1537 pjsua_perror(THIS_FILE, "Error creating SIP TCP listener",
1538 status);
1539 goto on_return;
1540 }
1541
1542 /* Save the transport */
1543 pjsua_var.tpdata[id].type = type;
1544 pjsua_var.tpdata[id].local_name = tcp->addr_name;
1545 pjsua_var.tpdata[id].data.factory = tcp;
1546
Benny Prijono3569c0d2007-04-06 10:29:20 +00001547#endif /* PJ_HAS_TCP */
1548
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001549#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
1550 } else if (type == PJSIP_TRANSPORT_TLS) {
1551 /*
1552 * Create TLS transport.
1553 */
Benny Prijonof3bbc132006-12-25 06:43:59 +00001554 /*
1555 * Create TCP transport.
1556 */
1557 pjsua_transport_config config;
1558 pjsip_host_port a_name;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001559 pjsip_tpfactory *tls;
Benny Prijonof3bbc132006-12-25 06:43:59 +00001560 pj_sockaddr_in local_addr;
1561
1562 /* Supply default config if it's not specified */
1563 if (cfg == NULL) {
1564 pjsua_transport_config_default(&config);
1565 config.port = 5061;
1566 cfg = &config;
1567 }
1568
1569 /* Init local address */
1570 pj_sockaddr_in_init(&local_addr, 0, 0);
1571
1572 if (cfg->port)
1573 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1574
1575 if (cfg->bound_addr.slen) {
1576 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1577 if (status != PJ_SUCCESS) {
1578 pjsua_perror(THIS_FILE,
1579 "Unable to resolve transport bound address",
1580 status);
1581 goto on_return;
1582 }
1583 }
1584
1585 /* Init published name */
1586 pj_bzero(&a_name, sizeof(pjsip_host_port));
1587 if (cfg->public_addr.slen)
1588 a_name.host = cfg->public_addr;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001589
1590 status = pjsip_tls_transport_start(pjsua_var.endpt,
Benny Prijonof3bbc132006-12-25 06:43:59 +00001591 &cfg->tls_setting,
1592 &local_addr, &a_name, 1, &tls);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001593 if (status != PJ_SUCCESS) {
1594 pjsua_perror(THIS_FILE, "Error creating SIP TLS listener",
1595 status);
1596 goto on_return;
1597 }
1598
1599 /* Save the transport */
1600 pjsua_var.tpdata[id].type = type;
1601 pjsua_var.tpdata[id].local_name = tls->addr_name;
1602 pjsua_var.tpdata[id].data.factory = tls;
1603#endif
1604
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001605 } else {
1606 status = PJSIP_EUNSUPTRANSPORT;
1607 pjsua_perror(THIS_FILE, "Error creating transport", status);
1608 goto on_return;
1609 }
1610
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001611
1612 /* Return the ID */
1613 if (p_id) *p_id = id;
1614
1615 status = PJ_SUCCESS;
1616
1617on_return:
1618
1619 PJSUA_UNLOCK();
1620
Benny Prijonod8410532006-06-15 11:04:33 +00001621 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001622}
1623
1624
1625/*
1626 * Register transport that has been created by application.
1627 */
1628PJ_DEF(pj_status_t) pjsua_transport_register( pjsip_transport *tp,
1629 pjsua_transport_id *p_id)
1630{
1631 unsigned id;
1632
1633 PJSUA_LOCK();
1634
1635 /* Find empty transport slot */
1636 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001637 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001638 break;
1639 }
1640
1641 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1642 pjsua_perror(THIS_FILE, "Error creating transport", PJ_ETOOMANY);
1643 PJSUA_UNLOCK();
1644 return PJ_ETOOMANY;
1645 }
1646
1647 /* Save the transport */
Benny Prijonod17a5e92007-05-29 11:51:45 +00001648 pjsua_var.tpdata[id].type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001649 pjsua_var.tpdata[id].local_name = tp->local_name;
1650 pjsua_var.tpdata[id].data.tp = tp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001651
1652 /* Return the ID */
1653 if (p_id) *p_id = id;
1654
1655 PJSUA_UNLOCK();
1656
1657 return PJ_SUCCESS;
1658}
1659
1660
1661/*
1662 * Enumerate all transports currently created in the system.
1663 */
1664PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[],
1665 unsigned *p_count )
1666{
1667 unsigned i, count;
1668
1669 PJSUA_LOCK();
1670
1671 for (i=0, count=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata) && count<*p_count;
1672 ++i)
1673 {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001674 if (!pjsua_var.tpdata[i].data.ptr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001675 continue;
1676
1677 id[count++] = i;
1678 }
1679
1680 *p_count = count;
1681
1682 PJSUA_UNLOCK();
1683
1684 return PJ_SUCCESS;
1685}
1686
1687
1688/*
1689 * Get information about transports.
1690 */
1691PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
1692 pjsua_transport_info *info)
1693{
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001694 pjsua_transport_data *t = &pjsua_var.tpdata[id];
Benny Prijono0a5cad82006-09-26 13:21:02 +00001695 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001696
Benny Prijonoac623b32006-07-03 15:19:31 +00001697 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001698
1699 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001700 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1701 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001702
1703 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001704 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001705
1706 PJSUA_LOCK();
1707
Benny Prijonoe93e2872006-06-28 16:46:49 +00001708 if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_UDP) {
1709
1710 pjsip_transport *tp = t->data.tp;
1711
1712 if (tp == NULL) {
1713 PJSUA_UNLOCK();
1714 return PJ_EINVALIDOP;
1715 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001716
Benny Prijonoe93e2872006-06-28 16:46:49 +00001717 info->id = id;
Benny Prijonod17a5e92007-05-29 11:51:45 +00001718 info->type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001719 info->type_name = pj_str(tp->type_name);
1720 info->info = pj_str(tp->info);
1721 info->flag = tp->flag;
1722 info->addr_len = tp->addr_len;
1723 info->local_addr = tp->local_addr;
1724 info->local_name = tp->local_name;
1725 info->usage_count = pj_atomic_get(tp->ref_cnt);
1726
Benny Prijono0a5cad82006-09-26 13:21:02 +00001727 status = PJ_SUCCESS;
1728
Benny Prijonoe93e2872006-06-28 16:46:49 +00001729 } else if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_TCP) {
1730
1731 pjsip_tpfactory *factory = t->data.factory;
1732
1733 if (factory == NULL) {
1734 PJSUA_UNLOCK();
1735 return PJ_EINVALIDOP;
1736 }
1737
1738 info->id = id;
1739 info->type = t->type;
1740 info->type_name = pj_str("TCP");
1741 info->info = pj_str("TCP transport");
1742 info->flag = factory->flag;
1743 info->addr_len = sizeof(factory->local_addr);
1744 info->local_addr = factory->local_addr;
1745 info->local_name = factory->addr_name;
1746 info->usage_count = 0;
1747
Benny Prijono0a5cad82006-09-26 13:21:02 +00001748 status = PJ_SUCCESS;
1749
1750 } else {
1751 pj_assert(!"Unsupported transport");
1752 status = PJ_EINVALIDOP;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001753 }
1754
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001755
1756 PJSUA_UNLOCK();
1757
Benny Prijono0a5cad82006-09-26 13:21:02 +00001758 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001759}
1760
1761
1762/*
1763 * Disable a transport or re-enable it.
1764 */
1765PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id,
1766 pj_bool_t enabled)
1767{
1768 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001769 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1770 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001771
1772 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001773 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001774
1775
1776 /* To be done!! */
1777 PJ_TODO(pjsua_transport_set_enable);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001778 PJ_UNUSED_ARG(enabled);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001779
1780 return PJ_EINVALIDOP;
1781}
1782
1783
1784/*
1785 * Close the transport.
1786 */
1787PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
1788 pj_bool_t force )
1789{
Benny Prijono5ff61872007-02-01 03:37:11 +00001790 pj_status_t status;
1791
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001792 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001793 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1794 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001795
1796 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001797 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001798
Benny Prijono0a5cad82006-09-26 13:21:02 +00001799 /* Note: destroy() may not work if there are objects still referencing
1800 * the transport.
1801 */
1802 if (force) {
1803 switch (pjsua_var.tpdata[id].type) {
1804 case PJSIP_TRANSPORT_UDP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001805 status = pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
1806 if (status != PJ_SUCCESS)
1807 return status;
1808 status = pjsip_transport_destroy(pjsua_var.tpdata[id].data.tp);
1809 if (status != PJ_SUCCESS)
1810 return status;
1811 break;
1812
1813 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001814 case PJSIP_TRANSPORT_TCP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001815 /* This will close the TCP listener, but existing TCP/TLS
1816 * connections (if any) will still linger
1817 */
1818 status = (*pjsua_var.tpdata[id].data.factory->destroy)
1819 (pjsua_var.tpdata[id].data.factory);
1820 if (status != PJ_SUCCESS)
1821 return status;
1822
Benny Prijono0a5cad82006-09-26 13:21:02 +00001823 break;
Benny Prijono5ff61872007-02-01 03:37:11 +00001824
Benny Prijono1ebd6142006-10-19 15:48:02 +00001825 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001826 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001827 }
1828
1829 } else {
Benny Prijono5ff61872007-02-01 03:37:11 +00001830 /* If force is not specified, transports will be closed at their
1831 * convenient time. However this will leak PJSUA-API transport
1832 * descriptors as PJSUA-API wouldn't know when exactly the
1833 * transport is closed thus it can't cleanup PJSUA transport
1834 * descriptor.
1835 */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001836 switch (pjsua_var.tpdata[id].type) {
1837 case PJSIP_TRANSPORT_UDP:
1838 return pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
Benny Prijono5ff61872007-02-01 03:37:11 +00001839 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001840 case PJSIP_TRANSPORT_TCP:
1841 return (*pjsua_var.tpdata[id].data.factory->destroy)
1842 (pjsua_var.tpdata[id].data.factory);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001843 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001844 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001845 }
1846 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001847
Benny Prijono5ff61872007-02-01 03:37:11 +00001848 /* Cleanup pjsua data when force is applied */
1849 if (force) {
1850 pjsua_var.tpdata[id].type = PJSIP_TRANSPORT_UNSPECIFIED;
1851 pjsua_var.tpdata[id].data.ptr = NULL;
1852 }
1853
1854 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001855}
1856
1857
1858/*
1859 * Add additional headers etc in msg_data specified by application
1860 * when sending requests.
1861 */
1862void pjsua_process_msg_data(pjsip_tx_data *tdata,
1863 const pjsua_msg_data *msg_data)
1864{
1865 pj_bool_t allow_body;
1866 const pjsip_hdr *hdr;
1867
Benny Prijono56315612006-07-18 14:39:40 +00001868 /* Always add User-Agent */
1869 if (pjsua_var.ua_cfg.user_agent.slen &&
1870 tdata->msg->type == PJSIP_REQUEST_MSG)
1871 {
1872 const pj_str_t STR_USER_AGENT = { "User-Agent", 10 };
1873 pjsip_hdr *h;
1874 h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool,
1875 &STR_USER_AGENT,
1876 &pjsua_var.ua_cfg.user_agent);
1877 pjsip_msg_add_hdr(tdata->msg, h);
1878 }
1879
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001880 if (!msg_data)
1881 return;
1882
1883 hdr = msg_data->hdr_list.next;
1884 while (hdr && hdr != &msg_data->hdr_list) {
1885 pjsip_hdr *new_hdr;
1886
Benny Prijonoa1e69682007-05-11 15:14:34 +00001887 new_hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001888 pjsip_msg_add_hdr(tdata->msg, new_hdr);
1889
1890 hdr = hdr->next;
1891 }
1892
1893 allow_body = (tdata->msg->body == NULL);
1894
1895 if (allow_body && msg_data->content_type.slen && msg_data->msg_body.slen) {
1896 pjsip_media_type ctype;
1897 pjsip_msg_body *body;
1898
1899 pjsua_parse_media_type(tdata->pool, &msg_data->content_type, &ctype);
1900 body = pjsip_msg_body_create(tdata->pool, &ctype.type, &ctype.subtype,
1901 &msg_data->msg_body);
1902 tdata->msg->body = body;
1903 }
1904}
1905
1906
1907/*
1908 * Add route_set to outgoing requests
1909 */
1910void pjsua_set_msg_route_set( pjsip_tx_data *tdata,
1911 const pjsip_route_hdr *route_set )
1912{
1913 const pjsip_route_hdr *r;
1914
1915 r = route_set->next;
1916 while (r != route_set) {
1917 pjsip_route_hdr *new_r;
1918
Benny Prijonoa1e69682007-05-11 15:14:34 +00001919 new_r = (pjsip_route_hdr*) pjsip_hdr_clone(tdata->pool, r);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001920 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)new_r);
1921
1922 r = r->next;
1923 }
1924}
1925
1926
1927/*
1928 * Simple version of MIME type parsing (it doesn't support parameters)
1929 */
1930void pjsua_parse_media_type( pj_pool_t *pool,
1931 const pj_str_t *mime,
1932 pjsip_media_type *media_type)
1933{
1934 pj_str_t tmp;
1935 char *pos;
1936
Benny Prijonoac623b32006-07-03 15:19:31 +00001937 pj_bzero(media_type, sizeof(*media_type));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001938
1939 pj_strdup_with_null(pool, &tmp, mime);
1940
1941 pos = pj_strchr(&tmp, '/');
1942 if (pos) {
1943 media_type->type.ptr = tmp.ptr;
1944 media_type->type.slen = (pos-tmp.ptr);
1945 media_type->subtype.ptr = pos+1;
1946 media_type->subtype.slen = tmp.ptr+tmp.slen-pos-1;
1947 } else {
1948 media_type->type = tmp;
1949 }
1950}
1951
1952
1953/*
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001954 * Internal function to init transport selector from transport id.
1955 */
1956void pjsua_init_tpselector(pjsua_transport_id tp_id,
1957 pjsip_tpselector *sel)
1958{
1959 pjsua_transport_data *tpdata;
1960 unsigned flag;
1961
1962 pj_bzero(sel, sizeof(*sel));
1963 if (tp_id == PJSUA_INVALID_ID)
1964 return;
1965
Benny Prijonoa1e69682007-05-11 15:14:34 +00001966 pj_assert(tp_id >= 0 && tp_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata));
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001967 tpdata = &pjsua_var.tpdata[tp_id];
1968
1969 flag = pjsip_transport_get_flag_from_type(tpdata->type);
1970
1971 if (flag & PJSIP_TRANSPORT_DATAGRAM) {
1972 sel->type = PJSIP_TPSELECTOR_TRANSPORT;
1973 sel->u.transport = tpdata->data.tp;
1974 } else {
1975 sel->type = PJSIP_TPSELECTOR_LISTENER;
1976 sel->u.listener = tpdata->data.factory;
1977 }
1978}
1979
1980
Benny Prijono6ba8c542007-10-16 01:34:14 +00001981/* Callback upon NAT detection completion */
1982static void nat_detect_cb(void *user_data,
1983 const pj_stun_nat_detect_result *res)
1984{
1985 PJ_UNUSED_ARG(user_data);
1986
1987 pjsua_var.nat_in_progress = PJ_FALSE;
1988 pjsua_var.nat_status = res->status;
1989 pjsua_var.nat_type = res->nat_type;
1990
1991 if (pjsua_var.ua_cfg.cb.on_nat_detect) {
1992 (*pjsua_var.ua_cfg.cb.on_nat_detect)(res);
1993 }
1994}
1995
1996
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001997/*
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001998 * Detect NAT type.
1999 */
Benny Prijono6ba8c542007-10-16 01:34:14 +00002000PJ_DEF(pj_status_t) pjsua_detect_nat_type()
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002001{
2002 pj_status_t status;
2003
Benny Prijono6ba8c542007-10-16 01:34:14 +00002004 if (pjsua_var.nat_in_progress)
2005 return PJ_SUCCESS;
2006
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002007 /* Make sure STUN server resolution has completed */
2008 status = pjsua_resolve_stun_server(PJ_TRUE);
2009 if (status != PJ_SUCCESS) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00002010 pjsua_var.nat_status = status;
2011 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002012 return status;
2013 }
2014
2015 /* Make sure we have STUN */
2016 if (pjsua_var.stun_srv.ipv4.sin_family == 0) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00002017 pjsua_var.nat_status = PJNATH_ESTUNINSERVER;
2018 return PJNATH_ESTUNINSERVER;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002019 }
2020
Benny Prijono6ba8c542007-10-16 01:34:14 +00002021 status = pj_stun_detect_nat_type(&pjsua_var.stun_srv.ipv4,
2022 &pjsua_var.stun_cfg,
2023 NULL, &nat_detect_cb);
2024
2025 if (status != PJ_SUCCESS) {
2026 pjsua_var.nat_status = status;
2027 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
2028 return status;
2029 }
2030
2031 pjsua_var.nat_in_progress = PJ_TRUE;
2032
2033 return PJ_SUCCESS;
2034}
2035
2036
2037/*
2038 * Get NAT type.
2039 */
2040PJ_DEF(pj_status_t) pjsua_get_nat_type(pj_stun_nat_type *type)
2041{
2042 *type = pjsua_var.nat_type;
2043 return pjsua_var.nat_status;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002044}
2045
2046
2047/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002048 * Verify that valid SIP url is given.
2049 */
2050PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url)
2051{
2052 pjsip_uri *p;
2053 pj_pool_t *pool;
2054 char *url;
2055 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
2056
2057 if (!len) return -1;
2058
2059 pool = pj_pool_create(&pjsua_var.cp.factory, "check%p", 1024, 0, NULL);
2060 if (!pool) return -1;
2061
Benny Prijonoa1e69682007-05-11 15:14:34 +00002062 url = (char*) pj_pool_alloc(pool, len+1);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002063 pj_ansi_strcpy(url, c_url);
2064
2065 p = pjsip_parse_uri(pool, url, len, 0);
Benny Prijonocf0b4b22007-10-06 17:31:09 +00002066 if (!p || (pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0 &&
2067 pj_stricmp2(pjsip_uri_get_scheme(p), "sips") != 0))
2068 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002069 p = NULL;
Benny Prijonocf0b4b22007-10-06 17:31:09 +00002070 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002071
2072 pj_pool_release(pool);
2073 return p ? 0 : -1;
2074}
Benny Prijonoda9785b2007-04-02 20:43:06 +00002075
2076
Benny Prijono91d06b62008-09-20 12:16:56 +00002077/**
2078 * Normalize route URI (check for ";lr" and append one if it doesn't
2079 * exist and pjsua_config.force_lr is set.
2080 */
2081pj_status_t normalize_route_uri(pj_pool_t *pool, pj_str_t *uri)
2082{
2083 pj_str_t tmp_uri;
2084 pj_pool_t *tmp_pool;
2085 pjsip_uri *uri_obj;
2086 pjsip_sip_uri *sip_uri;
2087
2088 tmp_pool = pjsua_pool_create("tmplr%p", 512, 512);
2089 if (!tmp_pool)
2090 return PJ_ENOMEM;
2091
2092 pj_strdup_with_null(tmp_pool, &tmp_uri, uri);
2093
2094 uri_obj = pjsip_parse_uri(tmp_pool, tmp_uri.ptr, tmp_uri.slen, 0);
2095 if (!uri_obj) {
2096 PJ_LOG(1,(THIS_FILE, "Invalid route URI: %.*s",
2097 (int)uri->slen, uri->ptr));
2098 pj_pool_release(tmp_pool);
2099 return PJSIP_EINVALIDURI;
2100 }
2101
2102 if (!PJSIP_URI_SCHEME_IS_SIP(uri_obj) &&
2103 !PJSIP_URI_SCHEME_IS_SIP(uri_obj))
2104 {
2105 PJ_LOG(1,(THIS_FILE, "Route URI must be SIP URI: %.*s",
2106 (int)uri->slen, uri->ptr));
2107 pj_pool_release(tmp_pool);
2108 return PJSIP_EINVALIDSCHEME;
2109 }
2110
2111 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri_obj);
2112
2113 /* Done if force_lr is disabled or if lr parameter is present */
2114 if (!pjsua_var.ua_cfg.force_lr || sip_uri->lr_param) {
2115 pj_pool_release(tmp_pool);
2116 return PJ_SUCCESS;
2117 }
2118
2119 /* Set lr param */
2120 sip_uri->lr_param = 1;
2121
2122 /* Print the URI */
2123 tmp_uri.ptr = (char*) pj_pool_alloc(tmp_pool, PJSIP_MAX_URL_SIZE);
2124 tmp_uri.slen = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, uri_obj,
2125 tmp_uri.ptr, PJSIP_MAX_URL_SIZE);
2126 if (tmp_uri.slen < 1) {
2127 PJ_LOG(1,(THIS_FILE, "Route URI is too long: %.*s",
2128 (int)uri->slen, uri->ptr));
2129 pj_pool_release(tmp_pool);
2130 return PJSIP_EURITOOLONG;
2131 }
2132
2133 /* Clone the URI */
2134 pj_strdup_with_null(pool, uri, &tmp_uri);
2135
2136 return PJ_SUCCESS;
2137}
2138
Benny Prijonoda9785b2007-04-02 20:43:06 +00002139/*
2140 * This is a utility function to dump the stack states to log, using
2141 * verbosity level 3.
2142 */
2143PJ_DEF(void) pjsua_dump(pj_bool_t detail)
2144{
2145 unsigned old_decor;
2146 unsigned i;
Benny Prijonoda9785b2007-04-02 20:43:06 +00002147
2148 PJ_LOG(3,(THIS_FILE, "Start dumping application states:"));
2149
2150 old_decor = pj_log_get_decor();
2151 pj_log_set_decor(old_decor & (PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
2152
2153 if (detail)
2154 pj_dump_config();
2155
2156 pjsip_endpt_dump(pjsua_get_pjsip_endpt(), detail);
2157
2158 pjmedia_endpt_dump(pjsua_get_pjmedia_endpt());
2159
2160 PJ_LOG(3,(THIS_FILE, "Dumping media transports:"));
2161 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2162 pjsua_call *call = &pjsua_var.calls[i];
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002163 pjmedia_transport_info tpinfo;
Benny Prijono5186eae2007-12-03 14:38:25 +00002164 char addr_buf[80];
Benny Prijonoda9785b2007-04-02 20:43:06 +00002165
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002166 /* MSVC complains about tpinfo not being initialized */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002167 //pj_bzero(&tpinfo, sizeof(tpinfo));
Benny Prijono4f093d22007-04-09 08:54:32 +00002168
Benny Prijono734fc2d2008-03-17 16:05:35 +00002169 pjmedia_transport_info_init(&tpinfo);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002170 pjmedia_transport_get_info(call->med_tp, &tpinfo);
Benny Prijonoda9785b2007-04-02 20:43:06 +00002171
Benny Prijono5186eae2007-12-03 14:38:25 +00002172 PJ_LOG(3,(THIS_FILE, " %s: %s",
Benny Prijonoda9785b2007-04-02 20:43:06 +00002173 (pjsua_var.media_cfg.enable_ice ? "ICE" : "UDP"),
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002174 pj_sockaddr_print(&tpinfo.sock_info.rtp_addr_name, addr_buf,
Benny Prijono5186eae2007-12-03 14:38:25 +00002175 sizeof(addr_buf), 3)));
Benny Prijonoda9785b2007-04-02 20:43:06 +00002176 }
2177
2178 pjsip_tsx_layer_dump(detail);
2179 pjsip_ua_dump(detail);
2180
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002181// Dumping complete call states may require a 'large' buffer
2182// (about 3KB per call session, including RTCP XR).
2183#if 0
Benny Prijonoda9785b2007-04-02 20:43:06 +00002184 /* Dump all invite sessions: */
2185 PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
2186
2187 if (pjsua_call_get_count() == 0) {
2188
2189 PJ_LOG(3,(THIS_FILE, " - no sessions -"));
2190
2191 } else {
2192 unsigned i;
2193
2194 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2195 if (pjsua_call_is_active(i)) {
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002196 /* Tricky logging, since call states log string tends to be
2197 * longer than PJ_LOG_MAX_SIZE.
2198 */
2199 char buf[1024 * 3];
2200 unsigned call_dump_len;
2201 unsigned part_len;
2202 unsigned part_idx;
2203 unsigned log_decor;
2204
Benny Prijonoda9785b2007-04-02 20:43:06 +00002205 pjsua_call_dump(i, detail, buf, sizeof(buf), " ");
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002206 call_dump_len = strlen(buf);
2207
2208 log_decor = pj_log_get_decor();
2209 pj_log_set_decor(log_decor & ~(PJ_LOG_HAS_NEWLINE |
2210 PJ_LOG_HAS_CR));
2211 PJ_LOG(3,(THIS_FILE, "\n"));
2212 pj_log_set_decor(0);
2213
2214 part_idx = 0;
2215 part_len = PJ_LOG_MAX_SIZE-80;
2216 while (part_idx < call_dump_len) {
2217 char p_orig, *p;
2218
2219 p = &buf[part_idx];
2220 if (part_idx + part_len > call_dump_len)
2221 part_len = call_dump_len - part_idx;
2222 p_orig = p[part_len];
2223 p[part_len] = '\0';
2224 PJ_LOG(3,(THIS_FILE, "%s", p));
2225 p[part_len] = p_orig;
2226 part_idx += part_len;
2227 }
2228 pj_log_set_decor(log_decor);
Benny Prijonoda9785b2007-04-02 20:43:06 +00002229 }
2230 }
2231 }
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002232#endif
Benny Prijonoda9785b2007-04-02 20:43:06 +00002233
2234 /* Dump presence status */
2235 pjsua_pres_dump(detail);
2236
2237 pj_log_set_decor(old_decor);
2238 PJ_LOG(3,(THIS_FILE, "Dump complete"));
2239}
2240