blob: 7430fc12d74383b945f74f44dd81e49a39092cf3 [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
Benny Prijono6627f3e2009-02-10 11:08:54 +000092 cfg->max_calls = ((PJSUA_MAX_CALLS) < 4) ? (PJSUA_MAX_CALLS) : 4;
Benny Prijono1f61a8f2007-08-16 10:11:44 +000093 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;
Benny Prijono10454dc2009-02-21 14:21:59 +0000180 cfg->snd_rec_latency = PJMEDIA_SND_DEFAULT_REC_LATENCY;
181 cfg->snd_play_latency = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000182 cfg->jb_init = cfg->jb_min_pre = cfg->jb_max_pre = cfg->jb_max = -1;
Benny Prijonof798e502009-03-09 13:08:16 +0000183 cfg->snd_auto_close_time = 1;
Benny Prijonof76e1392008-06-06 14:51:48 +0000184
185 cfg->turn_conn_type = PJ_TURN_TP_UDP;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000186}
187
Benny Prijonodc39fe82006-05-26 12:17:46 +0000188
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000189/*****************************************************************************
190 * This is a very simple PJSIP module, whose sole purpose is to display
191 * incoming and outgoing messages to log. This module will have priority
192 * higher than transport layer, which means:
193 *
194 * - incoming messages will come to this module first before reaching
195 * transaction layer.
196 *
197 * - outgoing messages will come to this module last, after the message
198 * has been 'printed' to contiguous buffer by transport layer and
199 * appropriate transport instance has been decided for this message.
200 *
201 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000202
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000203/* Notification on incoming messages */
204static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
205{
Benny Prijonob4a17c92006-07-10 14:40:21 +0000206 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
207 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000208 "--end msg--",
209 rdata->msg_info.len,
210 pjsip_rx_data_get_info(rdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000211 rdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000212 rdata->pkt_info.src_name,
213 rdata->pkt_info.src_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000214 (int)rdata->msg_info.len,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000215 rdata->msg_info.msg_buf));
216
217 /* Always return false, otherwise messages will not get processed! */
218 return PJ_FALSE;
219}
Benny Prijonodc39fe82006-05-26 12:17:46 +0000220
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000221/* Notification on outgoing messages */
222static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
223{
224
225 /* Important note:
226 * tp_info field is only valid after outgoing messages has passed
227 * transport layer. So don't try to access tp_info when the module
228 * has lower priority than transport layer.
229 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000230
Benny Prijonob4a17c92006-07-10 14:40:21 +0000231 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
232 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000233 "--end msg--",
234 (tdata->buf.cur - tdata->buf.start),
235 pjsip_tx_data_get_info(tdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000236 tdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000237 tdata->tp_info.dst_name,
238 tdata->tp_info.dst_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000239 (int)(tdata->buf.cur - tdata->buf.start),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000240 tdata->buf.start));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000241
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000242 /* Always return success, otherwise message will not get sent! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000243 return PJ_SUCCESS;
244}
245
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000246/* The module instance. */
247static pjsip_module pjsua_msg_logger =
248{
249 NULL, NULL, /* prev, next. */
250 { "mod-pjsua-log", 13 }, /* Name. */
251 -1, /* Id */
252 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
253 NULL, /* load() */
254 NULL, /* start() */
255 NULL, /* stop() */
256 NULL, /* unload() */
257 &logging_on_rx_msg, /* on_rx_request() */
258 &logging_on_rx_msg, /* on_rx_response() */
259 &logging_on_tx_msg, /* on_tx_request. */
260 &logging_on_tx_msg, /* on_tx_response() */
261 NULL, /* on_tsx_state() */
262
263};
264
265
266/*****************************************************************************
Benny Prijono56315612006-07-18 14:39:40 +0000267 * Another simple module to handle incoming OPTIONS request
268 */
269
270/* Notification on incoming request */
271static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
272{
273 pjsip_tx_data *tdata;
274 pjsip_response_addr res_addr;
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000275 pjmedia_transport_info tpinfo;
Benny Prijono56315612006-07-18 14:39:40 +0000276 pjmedia_sdp_session *sdp;
277 const pjsip_hdr *cap_hdr;
278 pj_status_t status;
279
280 /* Only want to handle OPTIONS requests */
281 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000282 pjsip_get_options_method()) != 0)
Benny Prijono56315612006-07-18 14:39:40 +0000283 {
284 return PJ_FALSE;
285 }
286
287 /* Create basic response. */
288 status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL,
289 &tdata);
290 if (status != PJ_SUCCESS) {
291 pjsua_perror(THIS_FILE, "Unable to create OPTIONS response", status);
292 return PJ_TRUE;
293 }
294
295 /* Add Allow header */
296 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ALLOW, NULL);
297 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000298 pjsip_msg_add_hdr(tdata->msg,
299 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000300 }
301
302 /* Add Accept header */
303 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL);
304 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000305 pjsip_msg_add_hdr(tdata->msg,
306 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000307 }
308
309 /* Add Supported header */
310 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_SUPPORTED, NULL);
311 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000312 pjsip_msg_add_hdr(tdata->msg,
313 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000314 }
315
316 /* Add Allow-Events header from the evsub module */
317 cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL);
318 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000319 pjsip_msg_add_hdr(tdata->msg,
320 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000321 }
322
323 /* Add User-Agent header */
324 if (pjsua_var.ua_cfg.user_agent.slen) {
325 const pj_str_t USER_AGENT = { "User-Agent", 10};
326 pjsip_hdr *h;
327
328 h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
329 &USER_AGENT,
330 &pjsua_var.ua_cfg.user_agent);
331 pjsip_msg_add_hdr(tdata->msg, h);
332 }
333
Benny Prijono617c5bc2007-04-02 19:51:21 +0000334 /* Get media socket info */
Benny Prijono734fc2d2008-03-17 16:05:35 +0000335 pjmedia_transport_info_init(&tpinfo);
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000336 pjmedia_transport_get_info(pjsua_var.calls[0].med_tp, &tpinfo);
Benny Prijono617c5bc2007-04-02 19:51:21 +0000337
Benny Prijono56315612006-07-18 14:39:40 +0000338 /* Add SDP body, using call0's RTP address */
339 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool, 1,
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000340 &tpinfo.sock_info, &sdp);
Benny Prijono56315612006-07-18 14:39:40 +0000341 if (status == PJ_SUCCESS) {
342 pjsip_create_sdp_body(tdata->pool, sdp, &tdata->msg->body);
343 }
344
345 /* Send response statelessly */
346 pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
347 status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, tdata, NULL, NULL);
348 if (status != PJ_SUCCESS)
349 pjsip_tx_data_dec_ref(tdata);
350
351 return PJ_TRUE;
352}
353
354
355/* The module instance. */
356static pjsip_module pjsua_options_handler =
357{
358 NULL, NULL, /* prev, next. */
359 { "mod-pjsua-options", 17 }, /* Name. */
360 -1, /* Id */
361 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
362 NULL, /* load() */
363 NULL, /* start() */
364 NULL, /* stop() */
365 NULL, /* unload() */
366 &options_on_rx_request, /* on_rx_request() */
367 NULL, /* on_rx_response() */
368 NULL, /* on_tx_request. */
369 NULL, /* on_tx_response() */
370 NULL, /* on_tsx_state() */
371
372};
373
374
375/*****************************************************************************
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000376 * These two functions are the main callbacks registered to PJSIP stack
377 * to receive SIP request and response messages that are outside any
378 * dialogs and any transactions.
379 */
Benny Prijono268ca612006-02-07 12:34:11 +0000380
381/*
382 * Handler for receiving incoming requests.
383 *
384 * This handler serves multiple purposes:
385 * - it receives requests outside dialogs.
386 * - it receives requests inside dialogs, when the requests are
387 * unhandled by other dialog usages. Example of these
388 * requests are: MESSAGE.
389 */
390static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
391{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000392 pj_bool_t processed = PJ_FALSE;
393
394 PJSUA_LOCK();
Benny Prijono38998232006-02-08 22:44:25 +0000395
Benny Prijono84126ab2006-02-09 09:30:09 +0000396 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
Benny Prijono38998232006-02-08 22:44:25 +0000397
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000398 processed = pjsua_call_on_incoming(rdata);
Benny Prijono38998232006-02-08 22:44:25 +0000399 }
400
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000401 PJSUA_UNLOCK();
402
403 return processed;
Benny Prijono268ca612006-02-07 12:34:11 +0000404}
405
406
407/*
408 * Handler for receiving incoming responses.
409 *
410 * This handler serves multiple purposes:
411 * - it receives strayed responses (i.e. outside any dialog and
412 * outside any transactions).
413 * - it receives responses coming to a transaction, when pjsua
414 * module is set as transaction user for the transaction.
415 * - it receives responses inside a dialog, when these responses
416 * are unhandled by other dialog usages.
417 */
418static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
419{
420 PJ_UNUSED_ARG(rdata);
Benny Prijono268ca612006-02-07 12:34:11 +0000421 return PJ_FALSE;
422}
423
424
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000425/*****************************************************************************
426 * Logging.
427 */
428
429/* Log callback */
430static void log_writer(int level, const char *buffer, int len)
Benny Prijono268ca612006-02-07 12:34:11 +0000431{
Benny Prijono572d4852006-11-23 21:50:02 +0000432 /* Write to file, stdout or application callback. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000433
434 if (pjsua_var.log_file) {
435 pj_ssize_t size = len;
436 pj_file_write(pjsua_var.log_file, buffer, &size);
Benny Prijono980ca0a2007-04-03 17:55:08 +0000437 /* This will slow things down considerably! Don't do it!
438 pj_file_flush(pjsua_var.log_file);
439 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000440 }
441
Benny Prijono572d4852006-11-23 21:50:02 +0000442 if (level <= (int)pjsua_var.log_cfg.console_level) {
443 if (pjsua_var.log_cfg.cb)
444 (*pjsua_var.log_cfg.cb)(level, buffer, len);
445 else
446 pj_log_write(level, buffer, len);
447 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000448}
449
450
451/*
452 * Application can call this function at any time (after pjsua_create(), of
453 * course) to change logging settings.
454 */
455PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg)
456{
457 pj_status_t status;
458
459 /* Save config. */
460 pjsua_logging_config_dup(pjsua_var.pool, &pjsua_var.log_cfg, cfg);
461
462 /* Redirect log function to ours */
463 pj_log_set_log_func( &log_writer );
464
Benny Prijono9cb09a22007-08-30 09:35:10 +0000465 /* Set decor */
466 pj_log_set_decor(pjsua_var.log_cfg.decor);
467
Benny Prijono4190cf92008-01-18 13:25:05 +0000468 /* Set log level */
469 pj_log_set_level(pjsua_var.log_cfg.level);
470
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000471 /* Close existing file, if any */
472 if (pjsua_var.log_file) {
473 pj_file_close(pjsua_var.log_file);
474 pjsua_var.log_file = NULL;
475 }
476
477 /* If output log file is desired, create the file: */
478 if (pjsua_var.log_cfg.log_filename.slen) {
479
480 status = pj_file_open(pjsua_var.pool,
481 pjsua_var.log_cfg.log_filename.ptr,
482 PJ_O_WRONLY,
483 &pjsua_var.log_file);
484
485 if (status != PJ_SUCCESS) {
486 pjsua_perror(THIS_FILE, "Error creating log file", status);
487 return status;
488 }
489 }
490
491 /* Unregister msg logging if it's previously registered */
492 if (pjsua_msg_logger.id >= 0) {
493 pjsip_endpt_unregister_module(pjsua_var.endpt, &pjsua_msg_logger);
494 pjsua_msg_logger.id = -1;
495 }
496
497 /* Enable SIP message logging */
498 if (pjsua_var.log_cfg.msg_logging)
499 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_msg_logger);
500
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000501 return PJ_SUCCESS;
502}
503
504
505/*****************************************************************************
506 * PJSUA Base API.
507 */
508
509/* Worker thread function. */
510static int worker_thread(void *arg)
511{
512 enum { TIMEOUT = 10 };
Benny Prijonof8baa872006-02-27 23:54:23 +0000513
Benny Prijono268ca612006-02-07 12:34:11 +0000514 PJ_UNUSED_ARG(arg);
515
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000516 while (!pjsua_var.thread_quit_flag) {
517 int count;
518
519 count = pjsua_handle_events(TIMEOUT);
520 if (count < 0)
521 pj_thread_sleep(TIMEOUT);
522 }
Benny Prijono268ca612006-02-07 12:34:11 +0000523
524 return 0;
525}
526
Benny Prijonodc39fe82006-05-26 12:17:46 +0000527
Benny Prijono8389c312008-02-21 21:36:34 +0000528/* Init random seed */
529static void init_random_seed(void)
530{
531 pj_sockaddr addr;
532 const pj_str_t *hostname;
533 pj_uint32_t pid;
534 pj_time_val t;
535 unsigned seed=0;
536
537 /* Add hostname */
538 hostname = pj_gethostname();
539 seed = pj_hash_calc(seed, hostname->ptr, (int)hostname->slen);
540
541 /* Add primary IP address */
542 if (pj_gethostip(pj_AF_INET(), &addr)==PJ_SUCCESS)
543 seed = pj_hash_calc(seed, &addr.ipv4.sin_addr, 4);
544
545 /* Get timeofday */
546 pj_gettimeofday(&t);
547 seed = pj_hash_calc(seed, &t, sizeof(t));
548
549 /* Add PID */
550 pid = pj_getpid();
551 seed = pj_hash_calc(seed, &pid, sizeof(pid));
552
553 /* Init random seed */
554 pj_srand(seed);
555}
556
Benny Prijono268ca612006-02-07 12:34:11 +0000557/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000558 * Instantiate pjsua application.
Benny Prijonodc39fe82006-05-26 12:17:46 +0000559 */
560PJ_DEF(pj_status_t) pjsua_create(void)
561{
562 pj_status_t status;
Benny Prijono268ca612006-02-07 12:34:11 +0000563
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000564 /* Init pjsua data */
565 init_data();
Benny Prijono268ca612006-02-07 12:34:11 +0000566
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000567 /* Set default logging settings */
568 pjsua_logging_config_default(&pjsua_var.log_cfg);
569
570 /* Init PJLIB: */
Benny Prijono268ca612006-02-07 12:34:11 +0000571 status = pj_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000572 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
573
Benny Prijono8389c312008-02-21 21:36:34 +0000574 /* Init random seed */
575 init_random_seed();
Benny Prijono268ca612006-02-07 12:34:11 +0000576
Benny Prijonofccab712006-02-22 22:23:22 +0000577 /* Init PJLIB-UTIL: */
Benny Prijonofccab712006-02-22 22:23:22 +0000578 status = pjlib_util_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000579 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonofccab712006-02-22 22:23:22 +0000580
Benny Prijonoec921342007-03-24 13:00:30 +0000581 /* Init PJNATH */
582 status = pjnath_init();
583 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono268ca612006-02-07 12:34:11 +0000584
Benny Prijono094d3ad2006-11-21 08:41:00 +0000585 /* Set default sound device ID */
Benny Prijono96e74f32009-02-22 12:00:12 +0000586 pjsua_var.cap_dev = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
587 pjsua_var.play_dev = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
Benny Prijono094d3ad2006-11-21 08:41:00 +0000588
Benny Prijono268ca612006-02-07 12:34:11 +0000589 /* Init caching pool. */
Benny Prijono106f5b72007-08-30 13:49:33 +0000590 pj_caching_pool_init(&pjsua_var.cp, NULL, 0);
Benny Prijono268ca612006-02-07 12:34:11 +0000591
592 /* Create memory pool for application. */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000593 pjsua_var.pool = pjsua_pool_create("pjsua", 1000, 1000);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000594
595 PJ_ASSERT_RETURN(pjsua_var.pool, PJ_ENOMEM);
Benny Prijono268ca612006-02-07 12:34:11 +0000596
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000597 /* Create mutex */
598 status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua",
599 &pjsua_var.mutex);
Benny Prijonoccf95622006-02-07 18:48:01 +0000600 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000601 pjsua_perror(THIS_FILE, "Unable to create mutex", status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000602 return status;
603 }
604
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000605 /* Must create SIP endpoint to initialize SIP parser. The parser
606 * is needed for example when application needs to call pjsua_verify_url().
Benny Prijonob8c25182006-04-29 08:31:09 +0000607 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000608 status = pjsip_endpt_create(&pjsua_var.cp.factory,
609 pj_gethostname()->ptr,
610 &pjsua_var.endpt);
611 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono39879152006-02-23 02:09:10 +0000612
613
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000614 return PJ_SUCCESS;
615}
616
617
618/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000619 * Initialize pjsua with the specified settings. All the settings are
620 * optional, and the default values will be used when the config is not
621 * specified.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000622 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000623PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
624 const pjsua_logging_config *log_cfg,
625 const pjsua_media_config *media_cfg)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000626{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000627 pjsua_config default_cfg;
628 pjsua_media_config default_media_cfg;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000629 const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
Benny Prijono3c5e28b2008-09-24 10:10:15 +0000630 pjsip_ua_init_param ua_init_param;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000631 pj_status_t status;
632
633
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000634 /* Create default configurations when the config is not supplied */
635
636 if (ua_cfg == NULL) {
637 pjsua_config_default(&default_cfg);
638 ua_cfg = &default_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000639 }
640
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000641 if (media_cfg == NULL) {
642 pjsua_media_config_default(&default_media_cfg);
643 media_cfg = &default_media_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000644 }
645
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000646 /* Initialize logging first so that info/errors can be captured */
647 if (log_cfg) {
648 status = pjsua_reconfigure_logging(log_cfg);
649 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000650 }
651
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000652 /* If nameserver is configured, create DNS resolver instance and
653 * set it to be used by SIP resolver.
654 */
655 if (ua_cfg->nameserver_count) {
656#if PJSIP_HAS_RESOLVER
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000657 unsigned i;
658
659 /* Create DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000660 status = pjsip_endpt_create_resolver(pjsua_var.endpt,
661 &pjsua_var.resolver);
Benny Prijono318f3842007-05-15 20:27:08 +0000662 if (status != PJ_SUCCESS) {
663 pjsua_perror(THIS_FILE, "Error creating resolver", status);
664 return status;
665 }
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000666
667 /* Configure nameserver for the DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000668 status = pj_dns_resolver_set_ns(pjsua_var.resolver,
669 ua_cfg->nameserver_count,
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000670 ua_cfg->nameserver, NULL);
671 if (status != PJ_SUCCESS) {
672 pjsua_perror(THIS_FILE, "Error setting nameserver", status);
673 return status;
674 }
675
676 /* Set this DNS resolver to be used by the SIP resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000677 status = pjsip_endpt_set_resolver(pjsua_var.endpt, pjsua_var.resolver);
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000678 if (status != PJ_SUCCESS) {
679 pjsua_perror(THIS_FILE, "Error setting DNS resolver", status);
680 return status;
681 }
682
683 /* Print nameservers */
684 for (i=0; i<ua_cfg->nameserver_count; ++i) {
685 PJ_LOG(4,(THIS_FILE, "Nameserver %.*s added",
686 (int)ua_cfg->nameserver[i].slen,
687 ua_cfg->nameserver[i].ptr));
688 }
689#else
690 PJ_LOG(2,(THIS_FILE,
691 "DNS resolver is disabled (PJSIP_HAS_RESOLVER==0)"));
692#endif
693 }
694
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000695 /* Init SIP UA: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000696
697 /* Initialize transaction layer: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000698 status = pjsip_tsx_layer_init_module(pjsua_var.endpt);
699 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000700
Benny Prijonodc39fe82006-05-26 12:17:46 +0000701
702 /* Initialize UA layer module: */
Benny Prijono3c5e28b2008-09-24 10:10:15 +0000703 pj_bzero(&ua_init_param, sizeof(ua_init_param));
704 if (ua_cfg->hangup_forked_call) {
705 ua_init_param.on_dlg_forked = &on_dlg_forked;
706 }
707 status = pjsip_ua_init_module( pjsua_var.endpt, &ua_init_param);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000708 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000709
Benny Prijonodc39fe82006-05-26 12:17:46 +0000710
Benny Prijono053f5222006-11-11 16:16:04 +0000711 /* Initialize Replaces support. */
712 status = pjsip_replaces_init_module( pjsua_var.endpt );
713 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
714
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000715 /* Initialize 100rel support */
716 status = pjsip_100rel_init_module(pjsua_var.endpt);
717 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono053f5222006-11-11 16:16:04 +0000718
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000719 /* Initialize and register PJSUA application module. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000720 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000721 const pjsip_module mod_initializer =
Benny Prijonodc39fe82006-05-26 12:17:46 +0000722 {
723 NULL, NULL, /* prev, next. */
724 { "mod-pjsua", 9 }, /* Name. */
725 -1, /* Id */
726 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
727 NULL, /* load() */
728 NULL, /* start() */
729 NULL, /* stop() */
730 NULL, /* unload() */
731 &mod_pjsua_on_rx_request, /* on_rx_request() */
732 &mod_pjsua_on_rx_response, /* on_rx_response() */
733 NULL, /* on_tx_request. */
734 NULL, /* on_tx_response() */
735 NULL, /* on_tsx_state() */
736 };
737
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000738 pjsua_var.mod = mod_initializer;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000739
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000740 status = pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_var.mod);
741 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000742 }
743
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000744
Benny Prijonodc39fe82006-05-26 12:17:46 +0000745
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000746 /* Initialize PJSUA call subsystem: */
747 status = pjsua_call_subsys_init(ua_cfg);
748 if (status != PJ_SUCCESS)
Benny Prijonodc39fe82006-05-26 12:17:46 +0000749 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000750
751
Benny Prijonoc97608e2007-03-23 16:34:20 +0000752 /* Start resolving STUN server */
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000753
Benny Prijonoc97608e2007-03-23 16:34:20 +0000754 status = pjsua_resolve_stun_server(PJ_FALSE);
755 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
756 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
757 return status;
758 }
759
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000760 /* Initialize PJSUA media subsystem */
761 status = pjsua_media_subsys_init(media_cfg);
762 if (status != PJ_SUCCESS)
763 goto on_error;
764
Benny Prijonodc39fe82006-05-26 12:17:46 +0000765
766 /* Init core SIMPLE module : */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000767 status = pjsip_evsub_init_module(pjsua_var.endpt);
768 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000769
Benny Prijonodc39fe82006-05-26 12:17:46 +0000770
771 /* Init presence module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000772 status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance());
773 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000774
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000775 /* Init PUBLISH module */
776 pjsip_publishc_init_module(pjsua_var.endpt);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000777
778 /* Init xfer/REFER module */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000779 status = pjsip_xfer_init_module( pjsua_var.endpt );
780 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000781
782 /* Init pjsua presence handler: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000783 status = pjsua_pres_init();
784 if (status != PJ_SUCCESS)
785 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000786
787 /* Init out-of-dialog MESSAGE request handler. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000788 status = pjsua_im_init();
789 if (status != PJ_SUCCESS)
790 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000791
Benny Prijonoc8141a82006-08-20 09:12:19 +0000792 /* Register OPTIONS handler */
793 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_options_handler);
794
795 /* Add OPTIONS in Allow header */
796 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_ALLOW,
797 NULL, 1, &STR_OPTIONS);
798
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000799 /* Start worker thread if needed. */
800 if (pjsua_var.ua_cfg.thread_cnt) {
801 unsigned i;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000802
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000803 if (pjsua_var.ua_cfg.thread_cnt > PJ_ARRAY_SIZE(pjsua_var.thread))
804 pjsua_var.ua_cfg.thread_cnt = PJ_ARRAY_SIZE(pjsua_var.thread);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000805
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000806 for (i=0; i<pjsua_var.ua_cfg.thread_cnt; ++i) {
807 status = pj_thread_create(pjsua_var.pool, "pjsua", &worker_thread,
808 NULL, 0, 0, &pjsua_var.thread[i]);
809 if (status != PJ_SUCCESS)
810 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000811 }
Benny Prijonof521eb02006-08-06 23:07:25 +0000812 PJ_LOG(4,(THIS_FILE, "%d SIP worker threads created",
813 pjsua_var.ua_cfg.thread_cnt));
814 } else {
815 PJ_LOG(4,(THIS_FILE, "No SIP worker threads created"));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000816 }
817
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000818 /* Done! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000819
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000820 PJ_LOG(3,(THIS_FILE, "pjsua version %s for %s initialized",
Benny Prijono106f5b72007-08-30 13:49:33 +0000821 pj_get_version(), PJ_OS_NAME));
Benny Prijonoccf95622006-02-07 18:48:01 +0000822
Benny Prijono268ca612006-02-07 12:34:11 +0000823 return PJ_SUCCESS;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000824
825on_error:
826 pjsua_destroy();
827 return status;
Benny Prijono268ca612006-02-07 12:34:11 +0000828}
829
830
Benny Prijono834aee32006-02-19 01:38:06 +0000831/* Sleep with polling */
832static void busy_sleep(unsigned msec)
833{
834 pj_time_val timeout, now;
835
836 pj_gettimeofday(&timeout);
837 timeout.msec += msec;
838 pj_time_val_normalize(&timeout);
839
840 do {
Nanang Izzuddin90b83202009-03-02 15:48:45 +0000841 int i;
842 i = msec / 10;
843 while (pjsua_handle_events(10) > 0 && i > 0)
844 --i;
Benny Prijono834aee32006-02-19 01:38:06 +0000845 pj_gettimeofday(&now);
846 } while (PJ_TIME_VAL_LT(now, timeout));
847}
848
Benny Prijonoebbf6892007-03-24 17:37:25 +0000849
Benny Prijonoebbf6892007-03-24 17:37:25 +0000850/*
851 * Callback function to receive notification from the resolver
852 * when the resolution process completes.
853 */
854static void stun_dns_srv_resolver_cb(void *user_data,
855 pj_status_t status,
856 const pj_dns_srv_record *rec)
857{
858 unsigned i;
859
860 PJ_UNUSED_ARG(user_data);
861
862 pjsua_var.stun_status = status;
863
864 if (status != PJ_SUCCESS) {
865 /* DNS SRV resolution failed. If stun_host is specified, resolve
866 * it with gethostbyname()
867 */
868 if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000869 pj_str_t str_host, str_port;
870 int port;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000871 pj_hostent he;
872
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000873 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
874 if (str_port.ptr != NULL) {
875 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
876 str_host.slen = (str_port.ptr - str_host.ptr);
877 str_port.ptr++;
878 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
879 str_host.slen - 1;
880 port = (int)pj_strtoul(&str_port);
881 if (port < 1 || port > 65535) {
882 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
883 pjsua_var.stun_status = PJ_EINVAL;
884 return;
885 }
886 } else {
887 str_host = pjsua_var.ua_cfg.stun_host;
888 port = 3478;
889 }
890
891 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000892
893 if (pjsua_var.stun_status == PJ_SUCCESS) {
894 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
895 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000896 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000897
898 PJ_LOG(3,(THIS_FILE,
899 "STUN server %.*s resolved, address is %s:%d",
900 (int)pjsua_var.ua_cfg.stun_host.slen,
901 pjsua_var.ua_cfg.stun_host.ptr,
902 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
903 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
904 }
905 } else {
906 char errmsg[PJ_ERR_MSG_SIZE];
907
908 pj_strerror(status, errmsg, sizeof(errmsg));
909 PJ_LOG(1,(THIS_FILE,
910 "DNS SRV resolution failed for STUN server %.*s: %s",
911 (int)pjsua_var.ua_cfg.stun_domain.slen,
912 pjsua_var.ua_cfg.stun_domain.ptr,
913 errmsg));
914 }
915 return;
916 }
917
Benny Prijonof5afa922007-06-11 16:51:18 +0000918 pj_assert(rec->count != 0 && rec->entry[0].server.addr_count != 0);
919 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL,
920 rec->entry[0].port);
921 pjsua_var.stun_srv.ipv4.sin_addr.s_addr =
922 rec->entry[0].server.addr[0].s_addr;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000923
924 PJ_LOG(3,(THIS_FILE, "_stun._udp.%.*s resolved, found %d entry(s):",
925 (int)pjsua_var.ua_cfg.stun_domain.slen,
926 pjsua_var.ua_cfg.stun_domain.ptr,
927 rec->count));
928
929 for (i=0; i<rec->count; ++i) {
930 PJ_LOG(3,(THIS_FILE,
931 " %d: prio=%d, weight=%d %s:%d",
932 i, rec->entry[i].priority, rec->entry[i].weight,
Benny Prijonof5afa922007-06-11 16:51:18 +0000933 pj_inet_ntoa(rec->entry[i].server.addr[0]),
934 (int)rec->entry[i].port));
Benny Prijonoebbf6892007-03-24 17:37:25 +0000935 }
936
937}
938
Benny Prijono268ca612006-02-07 12:34:11 +0000939/*
Benny Prijonoc97608e2007-03-23 16:34:20 +0000940 * Resolve STUN server.
941 */
942pj_status_t pjsua_resolve_stun_server(pj_bool_t wait)
943{
944 if (pjsua_var.stun_status == PJ_EUNKNOWN) {
945 /* Initialize STUN configuration */
946 pj_stun_config_init(&pjsua_var.stun_cfg, &pjsua_var.cp.factory, 0,
947 pjsip_endpt_get_ioqueue(pjsua_var.endpt),
948 pjsip_endpt_get_timer_heap(pjsua_var.endpt));
949
950 /* Start STUN server resolution */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000951
952 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000953
Benny Prijonoebbf6892007-03-24 17:37:25 +0000954 /* If stun_domain is specified, resolve STUN servers with DNS
955 * SRV resolution.
956 */
957 if (pjsua_var.ua_cfg.stun_domain.slen) {
958 pj_str_t res_type;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000959 pj_status_t status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000960
961 /* Fail if resolver is not configured */
962 if (pjsua_var.resolver == NULL) {
963 PJ_LOG(1,(THIS_FILE, "Nameserver must be configured when "
964 "stun_domain is specified"));
965 pjsua_var.stun_status = PJLIB_UTIL_EDNSNONS;
966 return PJLIB_UTIL_EDNSNONS;
967 }
968 res_type = pj_str("_stun._udp");
Benny Prijonoda9785b2007-04-02 20:43:06 +0000969 status =
Benny Prijonoebbf6892007-03-24 17:37:25 +0000970 pj_dns_srv_resolve(&pjsua_var.ua_cfg.stun_domain, &res_type,
971 3478, pjsua_var.pool, pjsua_var.resolver,
Benny Prijonof5afa922007-06-11 16:51:18 +0000972 0, NULL, &stun_dns_srv_resolver_cb, NULL);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000973 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +0000974 pjsua_perror(THIS_FILE, "Error starting DNS SRV resolution",
975 pjsua_var.stun_status);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000976 pjsua_var.stun_status = status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000977 return pjsua_var.stun_status;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000978 } else {
979 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000980 }
981 }
982 /* Otherwise if stun_host is specified, resolve STUN server with
983 * gethostbyname().
984 */
985 else if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000986 pj_str_t str_host, str_port;
987 int port;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000988 pj_hostent he;
989
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000990 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
991 if (str_port.ptr != NULL) {
992 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
993 str_host.slen = (str_port.ptr - str_host.ptr);
994 str_port.ptr++;
995 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
996 str_host.slen - 1;
997 port = (int)pj_strtoul(&str_port);
998 if (port < 1 || port > 65535) {
999 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
1000 pjsua_var.stun_status = PJ_EINVAL;
1001 return pjsua_var.stun_status;
1002 }
1003 } else {
1004 str_host = pjsua_var.ua_cfg.stun_host;
1005 port = 3478;
1006 }
1007
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001008 pjsua_var.stun_status =
1009 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, &str_host,
1010 (pj_uint16_t)port);
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001011
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001012 if (pjsua_var.stun_status != PJ_SUCCESS) {
1013 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001014
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001015 if (pjsua_var.stun_status == PJ_SUCCESS) {
1016 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
1017 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
1018 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
1019 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001020 }
Benny Prijonoebbf6892007-03-24 17:37:25 +00001021
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001022 PJ_LOG(3,(THIS_FILE,
1023 "STUN server %.*s resolved, address is %s:%d",
1024 (int)pjsua_var.ua_cfg.stun_host.slen,
1025 pjsua_var.ua_cfg.stun_host.ptr,
1026 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
1027 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
1028
Benny Prijonoc97608e2007-03-23 16:34:20 +00001029 }
Benny Prijonoebbf6892007-03-24 17:37:25 +00001030 /* Otherwise disable STUN. */
1031 else {
1032 pjsua_var.stun_status = PJ_SUCCESS;
1033 }
1034
1035
Benny Prijonoc97608e2007-03-23 16:34:20 +00001036 return pjsua_var.stun_status;
1037
1038 } else if (pjsua_var.stun_status == PJ_EPENDING) {
1039 /* STUN server resolution has been started, wait for the
1040 * result.
1041 */
Benny Prijonoebbf6892007-03-24 17:37:25 +00001042 if (wait) {
1043 while (pjsua_var.stun_status == PJ_EPENDING)
1044 pjsua_handle_events(10);
1045 }
1046
1047 return pjsua_var.stun_status;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001048
1049 } else {
1050 /* STUN server has been resolved, return the status */
1051 return pjsua_var.stun_status;
1052 }
1053}
1054
1055/*
Benny Prijono268ca612006-02-07 12:34:11 +00001056 * Destroy pjsua.
1057 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001058PJ_DEF(pj_status_t) pjsua_destroy(void)
Benny Prijono268ca612006-02-07 12:34:11 +00001059{
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001060 int i; /* Must be signed */
Benny Prijono268ca612006-02-07 12:34:11 +00001061
1062 /* Signal threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001063 pjsua_var.thread_quit_flag = 1;
Benny Prijono268ca612006-02-07 12:34:11 +00001064
1065 /* Wait worker threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001066 for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) {
1067 if (pjsua_var.thread[i]) {
1068 pj_thread_join(pjsua_var.thread[i]);
1069 pj_thread_destroy(pjsua_var.thread[i]);
1070 pjsua_var.thread[i] = NULL;
Benny Prijonoe4f2abb2006-02-10 14:04:05 +00001071 }
Benny Prijono268ca612006-02-07 12:34:11 +00001072 }
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001073
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001074 if (pjsua_var.endpt) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001075 /* Terminate all calls. */
1076 pjsua_call_hangup_all();
1077
Benny Prijono7f6ee022008-07-31 08:32:46 +00001078 /* Set all accounts to offline */
1079 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1080 if (!pjsua_var.acc[i].valid)
1081 continue;
1082 pjsua_var.acc[i].online_status = PJ_FALSE;
1083 pj_bzero(&pjsua_var.acc[i].rpid, sizeof(pjrpid_element));
1084 }
1085
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001086 /* Terminate all presence subscriptions. */
1087 pjsua_pres_shutdown();
1088
Benny Prijono4d1cc7b2008-07-14 09:55:01 +00001089 /* Unregister all accounts */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001090 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1091 if (!pjsua_var.acc[i].valid)
1092 continue;
1093
1094 if (pjsua_var.acc[i].regc) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001095 pjsua_acc_set_registration(i, PJ_FALSE);
1096 }
1097 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001098 }
Benny Prijono834aee32006-02-19 01:38:06 +00001099
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001100 /* Destroy media */
1101 pjsua_media_subsys_destroy();
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001102
Benny Prijono268ca612006-02-07 12:34:11 +00001103 /* Destroy endpoint. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001104 if (pjsua_var.endpt) {
Benny Prijonoff3b1462008-07-14 09:32:14 +00001105 /* Wait for some time to allow unregistration and ICE/TURN
1106 * transports shutdown to complete:
1107 */
1108 PJ_LOG(4,(THIS_FILE, "Shutting down..."));
1109 busy_sleep(1000);
1110
Benny Prijono4d1cc7b2008-07-14 09:55:01 +00001111 PJ_LOG(4,(THIS_FILE, "Destroying..."));
1112
Benny Prijonod7e26582008-07-18 23:51:49 +00001113 /* Must destroy endpoint first before destroying pools in
1114 * buddies or accounts, since shutting down transaction layer
1115 * may emit events which trigger some buddy or account callbacks
1116 * to be called.
1117 */
1118 pjsip_endpt_destroy(pjsua_var.endpt);
1119 pjsua_var.endpt = NULL;
1120
Benny Prijono4d1cc7b2008-07-14 09:55:01 +00001121 /* Destroy pool in the buddy object */
1122 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1123 if (pjsua_var.buddy[i].pool) {
1124 pj_pool_release(pjsua_var.buddy[i].pool);
1125 pjsua_var.buddy[i].pool = NULL;
1126 }
1127 }
1128
1129 /* Destroy accounts */
1130 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1131 if (pjsua_var.acc[i].pool) {
1132 pj_pool_release(pjsua_var.acc[i].pool);
1133 pjsua_var.acc[i].pool = NULL;
1134 }
1135 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001136 }
Benny Prijono268ca612006-02-07 12:34:11 +00001137
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001138 /* Destroy mutex */
1139 if (pjsua_var.mutex) {
1140 pj_mutex_destroy(pjsua_var.mutex);
1141 pjsua_var.mutex = NULL;
1142 }
1143
1144 /* Destroy pool and pool factory. */
1145 if (pjsua_var.pool) {
1146 pj_pool_release(pjsua_var.pool);
1147 pjsua_var.pool = NULL;
1148 pj_caching_pool_destroy(&pjsua_var.cp);
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00001149
1150 PJ_LOG(4,(THIS_FILE, "PJSUA destroyed..."));
1151
1152 /* End logging */
1153 if (pjsua_var.log_file) {
1154 pj_file_close(pjsua_var.log_file);
1155 pjsua_var.log_file = NULL;
1156 }
1157
1158 /* Shutdown PJLIB */
1159 pj_shutdown();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001160 }
Benny Prijono268ca612006-02-07 12:34:11 +00001161
Benny Prijonof762ee72006-12-01 11:14:37 +00001162 /* Clear pjsua_var */
1163 pj_bzero(&pjsua_var, sizeof(pjsua_var));
1164
Benny Prijono268ca612006-02-07 12:34:11 +00001165 /* Done. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001166 return PJ_SUCCESS;
1167}
1168
1169
1170/**
1171 * Application is recommended to call this function after all initialization
1172 * is done, so that the library can do additional checking set up
1173 * additional
1174 *
1175 * @return PJ_SUCCESS on success, or the appropriate error code.
1176 */
1177PJ_DEF(pj_status_t) pjsua_start(void)
1178{
1179 pj_status_t status;
1180
1181 status = pjsua_call_subsys_start();
1182 if (status != PJ_SUCCESS)
1183 return status;
1184
1185 status = pjsua_media_subsys_start();
1186 if (status != PJ_SUCCESS)
1187 return status;
1188
1189 status = pjsua_pres_start();
1190 if (status != PJ_SUCCESS)
1191 return status;
Benny Prijono268ca612006-02-07 12:34:11 +00001192
1193 return PJ_SUCCESS;
1194}
1195
Benny Prijono9fc735d2006-05-28 14:58:12 +00001196
1197/**
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001198 * Poll pjsua for events, and if necessary block the caller thread for
1199 * the specified maximum interval (in miliseconds).
1200 */
1201PJ_DEF(int) pjsua_handle_events(unsigned msec_timeout)
1202{
Benny Prijono897f9f82007-05-03 19:56:21 +00001203#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
Nanang Izzuddin82f7a412008-12-17 11:36:22 +00001204
1205 return pj_symbianos_poll(-1, msec_timeout);
1206
Benny Prijono897f9f82007-05-03 19:56:21 +00001207#else
1208
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001209 unsigned count = 0;
1210 pj_time_val tv;
1211 pj_status_t status;
1212
1213 tv.sec = 0;
1214 tv.msec = msec_timeout;
1215 pj_time_val_normalize(&tv);
1216
1217 status = pjsip_endpt_handle_events2(pjsua_var.endpt, &tv, &count);
1218
1219 if (status != PJ_SUCCESS)
1220 return -status;
1221
1222 return count;
Nanang Izzuddin82f7a412008-12-17 11:36:22 +00001223
Benny Prijono897f9f82007-05-03 19:56:21 +00001224#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001225}
1226
1227
1228/*
1229 * Create memory pool.
1230 */
1231PJ_DEF(pj_pool_t*) pjsua_pool_create( const char *name, pj_size_t init_size,
1232 pj_size_t increment)
1233{
1234 /* Pool factory is thread safe, no need to lock */
1235 return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment,
1236 NULL);
1237}
1238
1239
1240/*
1241 * Internal function to get SIP endpoint instance of pjsua, which is
1242 * needed for example to register module, create transports, etc.
1243 * Probably is only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001244 */
1245PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void)
1246{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001247 return pjsua_var.endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001248}
1249
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001250/*
1251 * Internal function to get media endpoint instance.
1252 * Only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001253 */
1254PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void)
1255{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001256 return pjsua_var.med_endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001257}
1258
Benny Prijono97b87172006-08-24 14:25:14 +00001259/*
1260 * Internal function to get PJSUA pool factory.
1261 */
1262PJ_DEF(pj_pool_factory*) pjsua_get_pool_factory(void)
1263{
1264 return &pjsua_var.cp.factory;
1265}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001266
1267/*****************************************************************************
1268 * PJSUA SIP Transport API.
1269 */
1270
1271/*
Benny Prijono23674a32007-12-01 08:59:25 +00001272 * Tools to get address string.
1273 */
1274static const char *addr_string(const pj_sockaddr_t *addr)
1275{
1276 static char str[128];
1277 str[0] = '\0';
1278 pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family,
1279 pj_sockaddr_get_addr(addr),
1280 str, sizeof(str));
1281 return str;
1282}
1283
1284/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001285 * Create and initialize SIP socket (and possibly resolve public
1286 * address via STUN, depending on config).
1287 */
Benny Prijono23674a32007-12-01 08:59:25 +00001288static pj_status_t create_sip_udp_sock(int af,
1289 const pj_str_t *bind_param,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001290 int port,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001291 pj_sock_t *p_sock,
Benny Prijono23674a32007-12-01 08:59:25 +00001292 pj_sockaddr *p_pub_addr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001293{
Benny Prijono23674a32007-12-01 08:59:25 +00001294 char stun_ip_addr[PJ_INET6_ADDRSTRLEN];
Benny Prijonoc97608e2007-03-23 16:34:20 +00001295 pj_str_t stun_srv;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001296 pj_sock_t sock;
Benny Prijono23674a32007-12-01 08:59:25 +00001297 pj_sockaddr bind_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001298 pj_status_t status;
1299
Benny Prijonoc97608e2007-03-23 16:34:20 +00001300 /* Make sure STUN server resolution has completed */
1301 status = pjsua_resolve_stun_server(PJ_TRUE);
1302 if (status != PJ_SUCCESS) {
1303 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
1304 return status;
1305 }
1306
Benny Prijono23674a32007-12-01 08:59:25 +00001307 /* Initialize bound address */
1308 if (bind_param->slen) {
1309 status = pj_sockaddr_init(af, &bind_addr, bind_param,
1310 (pj_uint16_t)port);
1311 if (status != PJ_SUCCESS) {
1312 pjsua_perror(THIS_FILE,
1313 "Unable to resolve transport bound address",
1314 status);
1315 return status;
1316 }
1317 } else {
1318 pj_sockaddr_init(af, &bind_addr, NULL, (pj_uint16_t)port);
1319 }
1320
1321 status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001322 if (status != PJ_SUCCESS) {
1323 pjsua_perror(THIS_FILE, "socket() error", status);
Benny Prijonod8410532006-06-15 11:04:33 +00001324 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001325 }
1326
Benny Prijono5f55a1f2007-12-05 05:08:29 +00001327 status = pj_sock_bind(sock, &bind_addr, pj_sockaddr_get_len(&bind_addr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001328 if (status != PJ_SUCCESS) {
1329 pjsua_perror(THIS_FILE, "bind() error", status);
1330 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001331 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001332 }
1333
Benny Prijonoe347cb02007-02-14 14:36:13 +00001334 /* If port is zero, get the bound port */
1335 if (port == 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001336 pj_sockaddr bound_addr;
Benny Prijonoe347cb02007-02-14 14:36:13 +00001337 int namelen = sizeof(bound_addr);
1338 status = pj_sock_getsockname(sock, &bound_addr, &namelen);
1339 if (status != PJ_SUCCESS) {
1340 pjsua_perror(THIS_FILE, "getsockname() error", status);
1341 pj_sock_close(sock);
1342 return status;
1343 }
1344
Benny Prijono23674a32007-12-01 08:59:25 +00001345 port = pj_sockaddr_get_port(&bound_addr);
Benny Prijonoe347cb02007-02-14 14:36:13 +00001346 }
1347
Benny Prijonoc97608e2007-03-23 16:34:20 +00001348 if (pjsua_var.stun_srv.addr.sa_family != 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001349 pj_ansi_strcpy(stun_ip_addr,pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
1350 stun_srv = pj_str(stun_ip_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001351 } else {
Benny Prijonoc97608e2007-03-23 16:34:20 +00001352 stun_srv.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001353 }
1354
1355 /* Get the published address, either by STUN or by resolving
1356 * the name of local host.
1357 */
Benny Prijono23674a32007-12-01 08:59:25 +00001358 if (pj_sockaddr_has_addr(p_pub_addr)) {
Benny Prijono744aca02007-11-11 03:06:07 +00001359 /*
1360 * Public address is already specified, no need to resolve the
1361 * address, only set the port.
1362 */
Benny Prijono23674a32007-12-01 08:59:25 +00001363 if (pj_sockaddr_get_port(p_pub_addr) == 0)
1364 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijono744aca02007-11-11 03:06:07 +00001365
1366 } else if (stun_srv.slen) {
Benny Prijono0a5cad82006-09-26 13:21:02 +00001367 /*
1368 * STUN is specified, resolve the address with STUN.
1369 */
Benny Prijono23674a32007-12-01 08:59:25 +00001370 if (af != pj_AF_INET()) {
1371 pjsua_perror(THIS_FILE, "Cannot use STUN", PJ_EAFNOTSUP);
1372 pj_sock_close(sock);
1373 return PJ_EAFNOTSUP;
1374 }
1375
Benny Prijono14c2b862007-02-21 00:40:05 +00001376 status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001377 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
1378 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
Benny Prijono23674a32007-12-01 08:59:25 +00001379 &p_pub_addr->ipv4);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001380 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +00001381 pjsua_perror(THIS_FILE, "Error contacting STUN server", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001382 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001383 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001384 }
1385
1386 } else {
Benny Prijono23674a32007-12-01 08:59:25 +00001387 pj_bzero(p_pub_addr, sizeof(pj_sockaddr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001388
Benny Prijono42d08d22007-12-20 11:23:07 +00001389 if (pj_sockaddr_has_addr(&bind_addr)) {
1390 pj_sockaddr_copy_addr(p_pub_addr, &bind_addr);
1391 } else {
1392 status = pj_gethostip(af, p_pub_addr);
1393 if (status != PJ_SUCCESS) {
1394 pjsua_perror(THIS_FILE, "Unable to get local host IP", status);
1395 pj_sock_close(sock);
1396 return status;
1397 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001398 }
1399
Benny Prijono23674a32007-12-01 08:59:25 +00001400 p_pub_addr->addr.sa_family = (pj_uint16_t)af;
1401 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001402 }
1403
1404 *p_sock = sock;
1405
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001406 PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
Benny Prijono23674a32007-12-01 08:59:25 +00001407 addr_string(p_pub_addr),
1408 (int)pj_sockaddr_get_port(p_pub_addr)));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001409
Benny Prijonod8410532006-06-15 11:04:33 +00001410 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001411}
1412
1413
1414/*
1415 * Create SIP transport.
1416 */
1417PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
1418 const pjsua_transport_config *cfg,
1419 pjsua_transport_id *p_id)
1420{
1421 pjsip_transport *tp;
1422 unsigned id;
1423 pj_status_t status;
1424
1425 PJSUA_LOCK();
1426
1427 /* Find empty transport slot */
1428 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001429 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001430 break;
1431 }
1432
1433 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1434 status = PJ_ETOOMANY;
1435 pjsua_perror(THIS_FILE, "Error creating transport", status);
1436 goto on_return;
1437 }
1438
1439 /* Create the transport */
Benny Prijonob2477142007-12-05 04:09:59 +00001440 if (type==PJSIP_TRANSPORT_UDP || type==PJSIP_TRANSPORT_UDP6) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001441 /*
Benny Prijono23674a32007-12-01 08:59:25 +00001442 * Create UDP transport (IPv4 or IPv6).
Benny Prijonoe93e2872006-06-28 16:46:49 +00001443 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001444 pjsua_transport_config config;
Benny Prijono23674a32007-12-01 08:59:25 +00001445 char hostbuf[PJ_INET6_ADDRSTRLEN];
Benny Prijonod8410532006-06-15 11:04:33 +00001446 pj_sock_t sock = PJ_INVALID_SOCKET;
Benny Prijono23674a32007-12-01 08:59:25 +00001447 pj_sockaddr pub_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001448 pjsip_host_port addr_name;
1449
1450 /* Supply default config if it's not specified */
1451 if (cfg == NULL) {
1452 pjsua_transport_config_default(&config);
1453 cfg = &config;
1454 }
1455
Benny Prijono0a5cad82006-09-26 13:21:02 +00001456 /* Initialize the public address from the config, if any */
Benny Prijono23674a32007-12-01 08:59:25 +00001457 pj_sockaddr_init(pjsip_transport_type_get_af(type), &pub_addr,
1458 NULL, (pj_uint16_t)cfg->port);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001459 if (cfg->public_addr.slen) {
Benny Prijono23674a32007-12-01 08:59:25 +00001460 status = pj_sockaddr_set_str_addr(pjsip_transport_type_get_af(type),
1461 &pub_addr, &cfg->public_addr);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001462 if (status != PJ_SUCCESS) {
1463 pjsua_perror(THIS_FILE,
1464 "Unable to resolve transport public address",
1465 status);
1466 goto on_return;
1467 }
1468 }
1469
1470 /* Create the socket and possibly resolve the address with STUN
1471 * (only when public address is not specified).
1472 */
Benny Prijono23674a32007-12-01 08:59:25 +00001473 status = create_sip_udp_sock(pjsip_transport_type_get_af(type),
1474 &cfg->bound_addr, cfg->port,
Benny Prijono0a5cad82006-09-26 13:21:02 +00001475 &sock, &pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001476 if (status != PJ_SUCCESS)
1477 goto on_return;
1478
Benny Prijono23674a32007-12-01 08:59:25 +00001479 pj_ansi_strcpy(hostbuf, addr_string(&pub_addr));
1480 addr_name.host = pj_str(hostbuf);
1481 addr_name.port = pj_sockaddr_get_port(&pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001482
1483 /* Create UDP transport */
Benny Prijono23674a32007-12-01 08:59:25 +00001484 status = pjsip_udp_transport_attach2(pjsua_var.endpt, type, sock,
1485 &addr_name, 1, &tp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001486 if (status != PJ_SUCCESS) {
1487 pjsua_perror(THIS_FILE, "Error creating SIP UDP transport",
1488 status);
1489 pj_sock_close(sock);
1490 goto on_return;
1491 }
1492
Benny Prijonoe93e2872006-06-28 16:46:49 +00001493
1494 /* Save the transport */
1495 pjsua_var.tpdata[id].type = type;
1496 pjsua_var.tpdata[id].local_name = tp->local_name;
1497 pjsua_var.tpdata[id].data.tp = tp;
1498
Benny Prijono3569c0d2007-04-06 10:29:20 +00001499#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
1500
Benny Prijonob2477142007-12-05 04:09:59 +00001501 } else if (type == PJSIP_TRANSPORT_TCP || type == PJSIP_TRANSPORT_TCP6) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001502 /*
1503 * Create TCP transport.
1504 */
1505 pjsua_transport_config config;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001506 pjsip_host_port a_name;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001507 pjsip_tpfactory *tcp;
1508 pj_sockaddr_in local_addr;
1509
1510 /* Supply default config if it's not specified */
1511 if (cfg == NULL) {
1512 pjsua_transport_config_default(&config);
1513 cfg = &config;
1514 }
1515
1516 /* Init local address */
1517 pj_sockaddr_in_init(&local_addr, 0, 0);
1518
1519 if (cfg->port)
1520 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1521
Benny Prijono0a5cad82006-09-26 13:21:02 +00001522 if (cfg->bound_addr.slen) {
1523 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1524 if (status != PJ_SUCCESS) {
1525 pjsua_perror(THIS_FILE,
1526 "Unable to resolve transport bound address",
1527 status);
1528 goto on_return;
1529 }
1530 }
1531
1532 /* Init published name */
1533 pj_bzero(&a_name, sizeof(pjsip_host_port));
1534 if (cfg->public_addr.slen)
1535 a_name.host = cfg->public_addr;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001536
1537 /* Create the TCP transport */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001538 status = pjsip_tcp_transport_start2(pjsua_var.endpt, &local_addr,
1539 &a_name, 1, &tcp);
Benny Prijonoe93e2872006-06-28 16:46:49 +00001540
1541 if (status != PJ_SUCCESS) {
1542 pjsua_perror(THIS_FILE, "Error creating SIP TCP listener",
1543 status);
1544 goto on_return;
1545 }
1546
1547 /* Save the transport */
1548 pjsua_var.tpdata[id].type = type;
1549 pjsua_var.tpdata[id].local_name = tcp->addr_name;
1550 pjsua_var.tpdata[id].data.factory = tcp;
1551
Benny Prijono3569c0d2007-04-06 10:29:20 +00001552#endif /* PJ_HAS_TCP */
1553
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001554#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
1555 } else if (type == PJSIP_TRANSPORT_TLS) {
1556 /*
1557 * Create TLS transport.
1558 */
Benny Prijonof3bbc132006-12-25 06:43:59 +00001559 /*
1560 * Create TCP transport.
1561 */
1562 pjsua_transport_config config;
1563 pjsip_host_port a_name;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001564 pjsip_tpfactory *tls;
Benny Prijonof3bbc132006-12-25 06:43:59 +00001565 pj_sockaddr_in local_addr;
1566
1567 /* Supply default config if it's not specified */
1568 if (cfg == NULL) {
1569 pjsua_transport_config_default(&config);
1570 config.port = 5061;
1571 cfg = &config;
1572 }
1573
1574 /* Init local address */
1575 pj_sockaddr_in_init(&local_addr, 0, 0);
1576
1577 if (cfg->port)
1578 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1579
1580 if (cfg->bound_addr.slen) {
1581 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1582 if (status != PJ_SUCCESS) {
1583 pjsua_perror(THIS_FILE,
1584 "Unable to resolve transport bound address",
1585 status);
1586 goto on_return;
1587 }
1588 }
1589
1590 /* Init published name */
1591 pj_bzero(&a_name, sizeof(pjsip_host_port));
1592 if (cfg->public_addr.slen)
1593 a_name.host = cfg->public_addr;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001594
1595 status = pjsip_tls_transport_start(pjsua_var.endpt,
Benny Prijonof3bbc132006-12-25 06:43:59 +00001596 &cfg->tls_setting,
1597 &local_addr, &a_name, 1, &tls);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001598 if (status != PJ_SUCCESS) {
1599 pjsua_perror(THIS_FILE, "Error creating SIP TLS listener",
1600 status);
1601 goto on_return;
1602 }
1603
1604 /* Save the transport */
1605 pjsua_var.tpdata[id].type = type;
1606 pjsua_var.tpdata[id].local_name = tls->addr_name;
1607 pjsua_var.tpdata[id].data.factory = tls;
1608#endif
1609
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001610 } else {
1611 status = PJSIP_EUNSUPTRANSPORT;
1612 pjsua_perror(THIS_FILE, "Error creating transport", status);
1613 goto on_return;
1614 }
1615
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001616
1617 /* Return the ID */
1618 if (p_id) *p_id = id;
1619
1620 status = PJ_SUCCESS;
1621
1622on_return:
1623
1624 PJSUA_UNLOCK();
1625
Benny Prijonod8410532006-06-15 11:04:33 +00001626 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001627}
1628
1629
1630/*
1631 * Register transport that has been created by application.
1632 */
1633PJ_DEF(pj_status_t) pjsua_transport_register( pjsip_transport *tp,
1634 pjsua_transport_id *p_id)
1635{
1636 unsigned id;
1637
1638 PJSUA_LOCK();
1639
1640 /* Find empty transport slot */
1641 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001642 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001643 break;
1644 }
1645
1646 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1647 pjsua_perror(THIS_FILE, "Error creating transport", PJ_ETOOMANY);
1648 PJSUA_UNLOCK();
1649 return PJ_ETOOMANY;
1650 }
1651
1652 /* Save the transport */
Benny Prijonod17a5e92007-05-29 11:51:45 +00001653 pjsua_var.tpdata[id].type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001654 pjsua_var.tpdata[id].local_name = tp->local_name;
1655 pjsua_var.tpdata[id].data.tp = tp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001656
1657 /* Return the ID */
1658 if (p_id) *p_id = id;
1659
1660 PJSUA_UNLOCK();
1661
1662 return PJ_SUCCESS;
1663}
1664
1665
1666/*
1667 * Enumerate all transports currently created in the system.
1668 */
1669PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[],
1670 unsigned *p_count )
1671{
1672 unsigned i, count;
1673
1674 PJSUA_LOCK();
1675
1676 for (i=0, count=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata) && count<*p_count;
1677 ++i)
1678 {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001679 if (!pjsua_var.tpdata[i].data.ptr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001680 continue;
1681
1682 id[count++] = i;
1683 }
1684
1685 *p_count = count;
1686
1687 PJSUA_UNLOCK();
1688
1689 return PJ_SUCCESS;
1690}
1691
1692
1693/*
1694 * Get information about transports.
1695 */
1696PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
1697 pjsua_transport_info *info)
1698{
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001699 pjsua_transport_data *t = &pjsua_var.tpdata[id];
Benny Prijono0a5cad82006-09-26 13:21:02 +00001700 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001701
Benny Prijonoac623b32006-07-03 15:19:31 +00001702 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001703
1704 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001705 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1706 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001707
1708 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001709 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001710
1711 PJSUA_LOCK();
1712
Benny Prijonoe93e2872006-06-28 16:46:49 +00001713 if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_UDP) {
1714
1715 pjsip_transport *tp = t->data.tp;
1716
1717 if (tp == NULL) {
1718 PJSUA_UNLOCK();
1719 return PJ_EINVALIDOP;
1720 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001721
Benny Prijonoe93e2872006-06-28 16:46:49 +00001722 info->id = id;
Benny Prijonod17a5e92007-05-29 11:51:45 +00001723 info->type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001724 info->type_name = pj_str(tp->type_name);
1725 info->info = pj_str(tp->info);
1726 info->flag = tp->flag;
1727 info->addr_len = tp->addr_len;
1728 info->local_addr = tp->local_addr;
1729 info->local_name = tp->local_name;
1730 info->usage_count = pj_atomic_get(tp->ref_cnt);
1731
Benny Prijono0a5cad82006-09-26 13:21:02 +00001732 status = PJ_SUCCESS;
1733
Benny Prijonoe93e2872006-06-28 16:46:49 +00001734 } else if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_TCP) {
1735
1736 pjsip_tpfactory *factory = t->data.factory;
1737
1738 if (factory == NULL) {
1739 PJSUA_UNLOCK();
1740 return PJ_EINVALIDOP;
1741 }
1742
1743 info->id = id;
1744 info->type = t->type;
1745 info->type_name = pj_str("TCP");
1746 info->info = pj_str("TCP transport");
1747 info->flag = factory->flag;
1748 info->addr_len = sizeof(factory->local_addr);
1749 info->local_addr = factory->local_addr;
1750 info->local_name = factory->addr_name;
1751 info->usage_count = 0;
1752
Benny Prijono0a5cad82006-09-26 13:21:02 +00001753 status = PJ_SUCCESS;
1754
1755 } else {
1756 pj_assert(!"Unsupported transport");
1757 status = PJ_EINVALIDOP;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001758 }
1759
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001760
1761 PJSUA_UNLOCK();
1762
Benny Prijono0a5cad82006-09-26 13:21:02 +00001763 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001764}
1765
1766
1767/*
1768 * Disable a transport or re-enable it.
1769 */
1770PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id,
1771 pj_bool_t enabled)
1772{
1773 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001774 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1775 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001776
1777 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001778 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001779
1780
1781 /* To be done!! */
1782 PJ_TODO(pjsua_transport_set_enable);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001783 PJ_UNUSED_ARG(enabled);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001784
1785 return PJ_EINVALIDOP;
1786}
1787
1788
1789/*
1790 * Close the transport.
1791 */
1792PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
1793 pj_bool_t force )
1794{
Benny Prijono5ff61872007-02-01 03:37:11 +00001795 pj_status_t status;
1796
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001797 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001798 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1799 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001800
1801 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001802 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001803
Benny Prijono0a5cad82006-09-26 13:21:02 +00001804 /* Note: destroy() may not work if there are objects still referencing
1805 * the transport.
1806 */
1807 if (force) {
1808 switch (pjsua_var.tpdata[id].type) {
1809 case PJSIP_TRANSPORT_UDP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001810 status = pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
1811 if (status != PJ_SUCCESS)
1812 return status;
1813 status = pjsip_transport_destroy(pjsua_var.tpdata[id].data.tp);
1814 if (status != PJ_SUCCESS)
1815 return status;
1816 break;
1817
1818 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001819 case PJSIP_TRANSPORT_TCP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001820 /* This will close the TCP listener, but existing TCP/TLS
1821 * connections (if any) will still linger
1822 */
1823 status = (*pjsua_var.tpdata[id].data.factory->destroy)
1824 (pjsua_var.tpdata[id].data.factory);
1825 if (status != PJ_SUCCESS)
1826 return status;
1827
Benny Prijono0a5cad82006-09-26 13:21:02 +00001828 break;
Benny Prijono5ff61872007-02-01 03:37:11 +00001829
Benny Prijono1ebd6142006-10-19 15:48:02 +00001830 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001831 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001832 }
1833
1834 } else {
Benny Prijono5ff61872007-02-01 03:37:11 +00001835 /* If force is not specified, transports will be closed at their
1836 * convenient time. However this will leak PJSUA-API transport
1837 * descriptors as PJSUA-API wouldn't know when exactly the
1838 * transport is closed thus it can't cleanup PJSUA transport
1839 * descriptor.
1840 */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001841 switch (pjsua_var.tpdata[id].type) {
1842 case PJSIP_TRANSPORT_UDP:
1843 return pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
Benny Prijono5ff61872007-02-01 03:37:11 +00001844 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001845 case PJSIP_TRANSPORT_TCP:
1846 return (*pjsua_var.tpdata[id].data.factory->destroy)
1847 (pjsua_var.tpdata[id].data.factory);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001848 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001849 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001850 }
1851 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001852
Benny Prijono5ff61872007-02-01 03:37:11 +00001853 /* Cleanup pjsua data when force is applied */
1854 if (force) {
1855 pjsua_var.tpdata[id].type = PJSIP_TRANSPORT_UNSPECIFIED;
1856 pjsua_var.tpdata[id].data.ptr = NULL;
1857 }
1858
1859 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001860}
1861
1862
1863/*
1864 * Add additional headers etc in msg_data specified by application
1865 * when sending requests.
1866 */
1867void pjsua_process_msg_data(pjsip_tx_data *tdata,
1868 const pjsua_msg_data *msg_data)
1869{
1870 pj_bool_t allow_body;
1871 const pjsip_hdr *hdr;
1872
Benny Prijono56315612006-07-18 14:39:40 +00001873 /* Always add User-Agent */
1874 if (pjsua_var.ua_cfg.user_agent.slen &&
1875 tdata->msg->type == PJSIP_REQUEST_MSG)
1876 {
1877 const pj_str_t STR_USER_AGENT = { "User-Agent", 10 };
1878 pjsip_hdr *h;
1879 h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool,
1880 &STR_USER_AGENT,
1881 &pjsua_var.ua_cfg.user_agent);
1882 pjsip_msg_add_hdr(tdata->msg, h);
1883 }
1884
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001885 if (!msg_data)
1886 return;
1887
1888 hdr = msg_data->hdr_list.next;
1889 while (hdr && hdr != &msg_data->hdr_list) {
1890 pjsip_hdr *new_hdr;
1891
Benny Prijonoa1e69682007-05-11 15:14:34 +00001892 new_hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001893 pjsip_msg_add_hdr(tdata->msg, new_hdr);
1894
1895 hdr = hdr->next;
1896 }
1897
1898 allow_body = (tdata->msg->body == NULL);
1899
1900 if (allow_body && msg_data->content_type.slen && msg_data->msg_body.slen) {
1901 pjsip_media_type ctype;
1902 pjsip_msg_body *body;
1903
1904 pjsua_parse_media_type(tdata->pool, &msg_data->content_type, &ctype);
1905 body = pjsip_msg_body_create(tdata->pool, &ctype.type, &ctype.subtype,
1906 &msg_data->msg_body);
1907 tdata->msg->body = body;
1908 }
1909}
1910
1911
1912/*
1913 * Add route_set to outgoing requests
1914 */
1915void pjsua_set_msg_route_set( pjsip_tx_data *tdata,
1916 const pjsip_route_hdr *route_set )
1917{
1918 const pjsip_route_hdr *r;
1919
1920 r = route_set->next;
1921 while (r != route_set) {
1922 pjsip_route_hdr *new_r;
1923
Benny Prijonoa1e69682007-05-11 15:14:34 +00001924 new_r = (pjsip_route_hdr*) pjsip_hdr_clone(tdata->pool, r);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001925 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)new_r);
1926
1927 r = r->next;
1928 }
1929}
1930
1931
1932/*
1933 * Simple version of MIME type parsing (it doesn't support parameters)
1934 */
1935void pjsua_parse_media_type( pj_pool_t *pool,
1936 const pj_str_t *mime,
1937 pjsip_media_type *media_type)
1938{
1939 pj_str_t tmp;
1940 char *pos;
1941
Benny Prijonoac623b32006-07-03 15:19:31 +00001942 pj_bzero(media_type, sizeof(*media_type));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001943
1944 pj_strdup_with_null(pool, &tmp, mime);
1945
1946 pos = pj_strchr(&tmp, '/');
1947 if (pos) {
1948 media_type->type.ptr = tmp.ptr;
1949 media_type->type.slen = (pos-tmp.ptr);
1950 media_type->subtype.ptr = pos+1;
1951 media_type->subtype.slen = tmp.ptr+tmp.slen-pos-1;
1952 } else {
1953 media_type->type = tmp;
1954 }
1955}
1956
1957
1958/*
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001959 * Internal function to init transport selector from transport id.
1960 */
1961void pjsua_init_tpselector(pjsua_transport_id tp_id,
1962 pjsip_tpselector *sel)
1963{
1964 pjsua_transport_data *tpdata;
1965 unsigned flag;
1966
1967 pj_bzero(sel, sizeof(*sel));
1968 if (tp_id == PJSUA_INVALID_ID)
1969 return;
1970
Benny Prijonoa1e69682007-05-11 15:14:34 +00001971 pj_assert(tp_id >= 0 && tp_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata));
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001972 tpdata = &pjsua_var.tpdata[tp_id];
1973
1974 flag = pjsip_transport_get_flag_from_type(tpdata->type);
1975
1976 if (flag & PJSIP_TRANSPORT_DATAGRAM) {
1977 sel->type = PJSIP_TPSELECTOR_TRANSPORT;
1978 sel->u.transport = tpdata->data.tp;
1979 } else {
1980 sel->type = PJSIP_TPSELECTOR_LISTENER;
1981 sel->u.listener = tpdata->data.factory;
1982 }
1983}
1984
1985
Benny Prijono6ba8c542007-10-16 01:34:14 +00001986/* Callback upon NAT detection completion */
1987static void nat_detect_cb(void *user_data,
1988 const pj_stun_nat_detect_result *res)
1989{
1990 PJ_UNUSED_ARG(user_data);
1991
1992 pjsua_var.nat_in_progress = PJ_FALSE;
1993 pjsua_var.nat_status = res->status;
1994 pjsua_var.nat_type = res->nat_type;
1995
1996 if (pjsua_var.ua_cfg.cb.on_nat_detect) {
1997 (*pjsua_var.ua_cfg.cb.on_nat_detect)(res);
1998 }
1999}
2000
2001
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002002/*
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002003 * Detect NAT type.
2004 */
Benny Prijono6ba8c542007-10-16 01:34:14 +00002005PJ_DEF(pj_status_t) pjsua_detect_nat_type()
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002006{
2007 pj_status_t status;
2008
Benny Prijono6ba8c542007-10-16 01:34:14 +00002009 if (pjsua_var.nat_in_progress)
2010 return PJ_SUCCESS;
2011
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002012 /* Make sure STUN server resolution has completed */
2013 status = pjsua_resolve_stun_server(PJ_TRUE);
2014 if (status != PJ_SUCCESS) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00002015 pjsua_var.nat_status = status;
2016 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002017 return status;
2018 }
2019
2020 /* Make sure we have STUN */
2021 if (pjsua_var.stun_srv.ipv4.sin_family == 0) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00002022 pjsua_var.nat_status = PJNATH_ESTUNINSERVER;
2023 return PJNATH_ESTUNINSERVER;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002024 }
2025
Benny Prijono6ba8c542007-10-16 01:34:14 +00002026 status = pj_stun_detect_nat_type(&pjsua_var.stun_srv.ipv4,
2027 &pjsua_var.stun_cfg,
2028 NULL, &nat_detect_cb);
2029
2030 if (status != PJ_SUCCESS) {
2031 pjsua_var.nat_status = status;
2032 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
2033 return status;
2034 }
2035
2036 pjsua_var.nat_in_progress = PJ_TRUE;
2037
2038 return PJ_SUCCESS;
2039}
2040
2041
2042/*
2043 * Get NAT type.
2044 */
2045PJ_DEF(pj_status_t) pjsua_get_nat_type(pj_stun_nat_type *type)
2046{
2047 *type = pjsua_var.nat_type;
2048 return pjsua_var.nat_status;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002049}
2050
2051
2052/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002053 * Verify that valid SIP url is given.
2054 */
2055PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url)
2056{
2057 pjsip_uri *p;
2058 pj_pool_t *pool;
2059 char *url;
2060 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
2061
2062 if (!len) return -1;
2063
2064 pool = pj_pool_create(&pjsua_var.cp.factory, "check%p", 1024, 0, NULL);
2065 if (!pool) return -1;
2066
Benny Prijonoa1e69682007-05-11 15:14:34 +00002067 url = (char*) pj_pool_alloc(pool, len+1);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002068 pj_ansi_strcpy(url, c_url);
2069
2070 p = pjsip_parse_uri(pool, url, len, 0);
Benny Prijonocf0b4b22007-10-06 17:31:09 +00002071 if (!p || (pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0 &&
2072 pj_stricmp2(pjsip_uri_get_scheme(p), "sips") != 0))
2073 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002074 p = NULL;
Benny Prijonocf0b4b22007-10-06 17:31:09 +00002075 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002076
2077 pj_pool_release(pool);
2078 return p ? 0 : -1;
2079}
Benny Prijonoda9785b2007-04-02 20:43:06 +00002080
2081
Benny Prijono91d06b62008-09-20 12:16:56 +00002082/**
2083 * Normalize route URI (check for ";lr" and append one if it doesn't
2084 * exist and pjsua_config.force_lr is set.
2085 */
2086pj_status_t normalize_route_uri(pj_pool_t *pool, pj_str_t *uri)
2087{
2088 pj_str_t tmp_uri;
2089 pj_pool_t *tmp_pool;
2090 pjsip_uri *uri_obj;
2091 pjsip_sip_uri *sip_uri;
2092
2093 tmp_pool = pjsua_pool_create("tmplr%p", 512, 512);
2094 if (!tmp_pool)
2095 return PJ_ENOMEM;
2096
2097 pj_strdup_with_null(tmp_pool, &tmp_uri, uri);
2098
2099 uri_obj = pjsip_parse_uri(tmp_pool, tmp_uri.ptr, tmp_uri.slen, 0);
2100 if (!uri_obj) {
2101 PJ_LOG(1,(THIS_FILE, "Invalid route URI: %.*s",
2102 (int)uri->slen, uri->ptr));
2103 pj_pool_release(tmp_pool);
2104 return PJSIP_EINVALIDURI;
2105 }
2106
2107 if (!PJSIP_URI_SCHEME_IS_SIP(uri_obj) &&
2108 !PJSIP_URI_SCHEME_IS_SIP(uri_obj))
2109 {
2110 PJ_LOG(1,(THIS_FILE, "Route URI must be SIP URI: %.*s",
2111 (int)uri->slen, uri->ptr));
2112 pj_pool_release(tmp_pool);
2113 return PJSIP_EINVALIDSCHEME;
2114 }
2115
2116 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri_obj);
2117
2118 /* Done if force_lr is disabled or if lr parameter is present */
2119 if (!pjsua_var.ua_cfg.force_lr || sip_uri->lr_param) {
2120 pj_pool_release(tmp_pool);
2121 return PJ_SUCCESS;
2122 }
2123
2124 /* Set lr param */
2125 sip_uri->lr_param = 1;
2126
2127 /* Print the URI */
2128 tmp_uri.ptr = (char*) pj_pool_alloc(tmp_pool, PJSIP_MAX_URL_SIZE);
2129 tmp_uri.slen = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, uri_obj,
2130 tmp_uri.ptr, PJSIP_MAX_URL_SIZE);
2131 if (tmp_uri.slen < 1) {
2132 PJ_LOG(1,(THIS_FILE, "Route URI is too long: %.*s",
2133 (int)uri->slen, uri->ptr));
2134 pj_pool_release(tmp_pool);
2135 return PJSIP_EURITOOLONG;
2136 }
2137
2138 /* Clone the URI */
2139 pj_strdup_with_null(pool, uri, &tmp_uri);
2140
2141 return PJ_SUCCESS;
2142}
2143
Benny Prijonoda9785b2007-04-02 20:43:06 +00002144/*
2145 * This is a utility function to dump the stack states to log, using
2146 * verbosity level 3.
2147 */
2148PJ_DEF(void) pjsua_dump(pj_bool_t detail)
2149{
2150 unsigned old_decor;
2151 unsigned i;
Benny Prijonoda9785b2007-04-02 20:43:06 +00002152
2153 PJ_LOG(3,(THIS_FILE, "Start dumping application states:"));
2154
2155 old_decor = pj_log_get_decor();
2156 pj_log_set_decor(old_decor & (PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
2157
2158 if (detail)
2159 pj_dump_config();
2160
2161 pjsip_endpt_dump(pjsua_get_pjsip_endpt(), detail);
2162
2163 pjmedia_endpt_dump(pjsua_get_pjmedia_endpt());
2164
2165 PJ_LOG(3,(THIS_FILE, "Dumping media transports:"));
2166 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2167 pjsua_call *call = &pjsua_var.calls[i];
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002168 pjmedia_transport_info tpinfo;
Benny Prijono5186eae2007-12-03 14:38:25 +00002169 char addr_buf[80];
Benny Prijonoda9785b2007-04-02 20:43:06 +00002170
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002171 /* MSVC complains about tpinfo not being initialized */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002172 //pj_bzero(&tpinfo, sizeof(tpinfo));
Benny Prijono4f093d22007-04-09 08:54:32 +00002173
Benny Prijono734fc2d2008-03-17 16:05:35 +00002174 pjmedia_transport_info_init(&tpinfo);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002175 pjmedia_transport_get_info(call->med_tp, &tpinfo);
Benny Prijonoda9785b2007-04-02 20:43:06 +00002176
Benny Prijono5186eae2007-12-03 14:38:25 +00002177 PJ_LOG(3,(THIS_FILE, " %s: %s",
Benny Prijonoda9785b2007-04-02 20:43:06 +00002178 (pjsua_var.media_cfg.enable_ice ? "ICE" : "UDP"),
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002179 pj_sockaddr_print(&tpinfo.sock_info.rtp_addr_name, addr_buf,
Benny Prijono5186eae2007-12-03 14:38:25 +00002180 sizeof(addr_buf), 3)));
Benny Prijonoda9785b2007-04-02 20:43:06 +00002181 }
2182
2183 pjsip_tsx_layer_dump(detail);
2184 pjsip_ua_dump(detail);
2185
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002186// Dumping complete call states may require a 'large' buffer
2187// (about 3KB per call session, including RTCP XR).
2188#if 0
Benny Prijonoda9785b2007-04-02 20:43:06 +00002189 /* Dump all invite sessions: */
2190 PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
2191
2192 if (pjsua_call_get_count() == 0) {
2193
2194 PJ_LOG(3,(THIS_FILE, " - no sessions -"));
2195
2196 } else {
2197 unsigned i;
2198
2199 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2200 if (pjsua_call_is_active(i)) {
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002201 /* Tricky logging, since call states log string tends to be
2202 * longer than PJ_LOG_MAX_SIZE.
2203 */
2204 char buf[1024 * 3];
2205 unsigned call_dump_len;
2206 unsigned part_len;
2207 unsigned part_idx;
2208 unsigned log_decor;
2209
Benny Prijonoda9785b2007-04-02 20:43:06 +00002210 pjsua_call_dump(i, detail, buf, sizeof(buf), " ");
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002211 call_dump_len = strlen(buf);
2212
2213 log_decor = pj_log_get_decor();
2214 pj_log_set_decor(log_decor & ~(PJ_LOG_HAS_NEWLINE |
2215 PJ_LOG_HAS_CR));
2216 PJ_LOG(3,(THIS_FILE, "\n"));
2217 pj_log_set_decor(0);
2218
2219 part_idx = 0;
2220 part_len = PJ_LOG_MAX_SIZE-80;
2221 while (part_idx < call_dump_len) {
2222 char p_orig, *p;
2223
2224 p = &buf[part_idx];
2225 if (part_idx + part_len > call_dump_len)
2226 part_len = call_dump_len - part_idx;
2227 p_orig = p[part_len];
2228 p[part_len] = '\0';
2229 PJ_LOG(3,(THIS_FILE, "%s", p));
2230 p[part_len] = p_orig;
2231 part_idx += part_len;
2232 }
2233 pj_log_set_decor(log_decor);
Benny Prijonoda9785b2007-04-02 20:43:06 +00002234 }
2235 }
2236 }
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002237#endif
Benny Prijonoda9785b2007-04-02 20:43:06 +00002238
2239 /* Dump presence status */
2240 pjsua_pres_dump(detail);
2241
2242 pj_log_set_decor(old_decor);
2243 PJ_LOG(3,(THIS_FILE, "Dump complete"));
2244}
2245