blob: 760f75055f4e744358d263244161d13926da7b36 [file] [log] [blame]
Benny Prijono268ca612006-02-07 12:34:11 +00001/* $Id$ */
2/*
Benny Prijono32177c02008-06-20 22:44:47 +00003 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono268ca612006-02-07 12:34:11 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000020#include <pjsua-lib/pjsua_internal.h>
Benny Prijono268ca612006-02-07 12:34:11 +000021
Benny Prijono268ca612006-02-07 12:34:11 +000022
Benny Prijono84126ab2006-02-09 09:30:09 +000023#define THIS_FILE "pjsua_core.c"
Benny Prijono268ca612006-02-07 12:34:11 +000024
25
Benny Prijonoeebe9af2006-06-13 22:57:13 +000026/* PJSUA application instance. */
27struct pjsua_data pjsua_var;
Benny Prijono84126ab2006-02-09 09:30:09 +000028
29
Benny Prijono44e88ea2007-10-30 15:42:35 +000030PJ_DEF(struct pjsua_data*) pjsua_get_var(void)
31{
32 return &pjsua_var;
33}
34
35
Benny Prijonoeebe9af2006-06-13 22:57:13 +000036/* Display error */
37PJ_DEF(void) pjsua_perror( const char *sender, const char *title,
38 pj_status_t status)
Benny Prijono268ca612006-02-07 12:34:11 +000039{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000040 char errmsg[PJ_ERR_MSG_SIZE];
Benny Prijonoa91a0032006-02-26 21:23:45 +000041
Benny Prijonoeebe9af2006-06-13 22:57:13 +000042 pj_strerror(status, errmsg, sizeof(errmsg));
Benny Prijonod6e362a2008-07-19 17:53:47 +000043 PJ_LOG(1,(sender, "%s: %s [status=%d]", title, errmsg, status));
Benny Prijono268ca612006-02-07 12:34:11 +000044}
45
46
Benny Prijonoeebe9af2006-06-13 22:57:13 +000047static void init_data()
Benny Prijonodc39fe82006-05-26 12:17:46 +000048{
49 unsigned i;
50
Benny Prijonoc97608e2007-03-23 16:34:20 +000051 pj_bzero(&pjsua_var, sizeof(pjsua_var));
52
Benny Prijonoeebe9af2006-06-13 22:57:13 +000053 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i)
54 pjsua_var.acc[i].index = i;
55
56 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i)
57 pjsua_var.tpdata[i].index = i;
Benny Prijonoc97608e2007-03-23 16:34:20 +000058
59 pjsua_var.stun_status = PJ_EUNKNOWN;
Benny Prijono6ba8c542007-10-16 01:34:14 +000060 pjsua_var.nat_status = PJ_EPENDING;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000061}
Benny Prijonodc39fe82006-05-26 12:17:46 +000062
63
Benny Prijono1f61a8f2007-08-16 10:11:44 +000064PJ_DEF(void) pjsua_logging_config_default(pjsua_logging_config *cfg)
65{
66 pj_bzero(cfg, sizeof(*cfg));
67
68 cfg->msg_logging = PJ_TRUE;
69 cfg->level = 5;
70 cfg->console_level = 4;
71 cfg->decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME |
Benny Prijonod6e362a2008-07-19 17:53:47 +000072 PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE |
73 PJ_LOG_HAS_SPACE;
74#if defined(PJ_WIN32) && PJ_WIN32 != 0
75 cfg->decor |= PJ_LOG_HAS_COLOR;
76#endif
Benny Prijono1f61a8f2007-08-16 10:11:44 +000077}
78
79PJ_DEF(void) pjsua_logging_config_dup(pj_pool_t *pool,
80 pjsua_logging_config *dst,
81 const pjsua_logging_config *src)
82{
83 pj_memcpy(dst, src, sizeof(*src));
84 pj_strdup_with_null(pool, &dst->log_filename, &src->log_filename);
85}
86
87PJ_DEF(void) pjsua_config_default(pjsua_config *cfg)
88{
89 pj_bzero(cfg, sizeof(*cfg));
90
91 cfg->max_calls = 4;
92 cfg->thread_cnt = 1;
Benny Prijono91a6a172007-10-31 08:59:29 +000093 cfg->nat_type_in_sdp = 1;
Benny Prijonod8179652008-01-23 20:39:07 +000094#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
95 cfg->use_srtp = PJSUA_DEFAULT_USE_SRTP;
96 cfg->srtp_secure_signaling = PJSUA_DEFAULT_SRTP_SECURE_SIGNALING;
97#endif
Benny Prijono1f61a8f2007-08-16 10:11:44 +000098}
99
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000100PJ_DEF(void) pjsua_config_dup(pj_pool_t *pool,
101 pjsua_config *dst,
102 const pjsua_config *src)
103{
104 unsigned i;
105
106 pj_memcpy(dst, src, sizeof(*src));
107
108 for (i=0; i<src->outbound_proxy_cnt; ++i) {
109 pj_strdup_with_null(pool, &dst->outbound_proxy[i],
110 &src->outbound_proxy[i]);
111 }
112
113 for (i=0; i<src->cred_count; ++i) {
114 pjsip_cred_dup(pool, &dst->cred_info[i], &src->cred_info[i]);
115 }
116
117 pj_strdup_with_null(pool, &dst->user_agent, &src->user_agent);
118 pj_strdup_with_null(pool, &dst->stun_domain, &src->stun_domain);
119 pj_strdup_with_null(pool, &dst->stun_host, &src->stun_host);
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000120}
121
122PJ_DEF(void) pjsua_msg_data_init(pjsua_msg_data *msg_data)
123{
124 pj_bzero(msg_data, sizeof(*msg_data));
125 pj_list_init(&msg_data->hdr_list);
126}
127
128PJ_DEF(void) pjsua_transport_config_default(pjsua_transport_config *cfg)
129{
130 pj_bzero(cfg, sizeof(*cfg));
131 pjsip_tls_setting_default(&cfg->tls_setting);
132}
133
134PJ_DEF(void) pjsua_transport_config_dup(pj_pool_t *pool,
135 pjsua_transport_config *dst,
136 const pjsua_transport_config *src)
137{
138 PJ_UNUSED_ARG(pool);
139 pj_memcpy(dst, src, sizeof(*src));
140}
141
142PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg)
143{
144 pj_bzero(cfg, sizeof(*cfg));
145
146 cfg->reg_timeout = PJSUA_REG_INTERVAL;
147 cfg->transport_id = PJSUA_INVALID_ID;
Benny Prijonoe8554ef2008-03-22 09:33:52 +0000148 cfg->allow_contact_rewrite = PJ_TRUE;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000149 cfg->require_100rel = pjsua_var.ua_cfg.require_100rel;
Benny Prijonobddef2c2007-10-31 13:28:08 +0000150 cfg->ka_interval = 15;
151 cfg->ka_data = pj_str("\r\n");
Benny Prijonod8179652008-01-23 20:39:07 +0000152#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
153 cfg->use_srtp = pjsua_var.ua_cfg.use_srtp;
154 cfg->srtp_secure_signaling = pjsua_var.ua_cfg.srtp_secure_signaling;
155#endif
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000156}
157
158PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg)
159{
160 pj_bzero(cfg, sizeof(*cfg));
161}
162
163PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg)
164{
165 pj_bzero(cfg, sizeof(*cfg));
166
167 cfg->clock_rate = PJSUA_DEFAULT_CLOCK_RATE;
Benny Prijono50f19b32008-03-11 13:15:43 +0000168 cfg->snd_clock_rate = 0;
Benny Prijono7d60d052008-03-29 12:24:20 +0000169 cfg->channel_count = 1;
Benny Prijono37c710b2008-01-10 12:09:26 +0000170 cfg->audio_frame_ptime = PJSUA_DEFAULT_AUDIO_FRAME_PTIME;
171 cfg->max_media_ports = PJSUA_MAX_CONF_PORTS;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000172 cfg->has_ioqueue = PJ_TRUE;
173 cfg->thread_cnt = 1;
174 cfg->quality = PJSUA_DEFAULT_CODEC_QUALITY;
175 cfg->ilbc_mode = PJSUA_DEFAULT_ILBC_MODE;
176 cfg->ec_tail_len = PJSUA_DEFAULT_EC_TAIL_LEN;
177 cfg->jb_init = cfg->jb_min_pre = cfg->jb_max_pre = cfg->jb_max = -1;
Nanang Izzuddin68559c32008-06-13 17:01:46 +0000178 cfg->snd_auto_close_time = -1;
Benny Prijonof76e1392008-06-06 14:51:48 +0000179
180 cfg->turn_conn_type = PJ_TURN_TP_UDP;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000181}
182
Benny Prijonodc39fe82006-05-26 12:17:46 +0000183
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000184/*****************************************************************************
185 * This is a very simple PJSIP module, whose sole purpose is to display
186 * incoming and outgoing messages to log. This module will have priority
187 * higher than transport layer, which means:
188 *
189 * - incoming messages will come to this module first before reaching
190 * transaction layer.
191 *
192 * - outgoing messages will come to this module last, after the message
193 * has been 'printed' to contiguous buffer by transport layer and
194 * appropriate transport instance has been decided for this message.
195 *
196 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000197
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000198/* Notification on incoming messages */
199static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
200{
Benny Prijonob4a17c92006-07-10 14:40:21 +0000201 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
202 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000203 "--end msg--",
204 rdata->msg_info.len,
205 pjsip_rx_data_get_info(rdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000206 rdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000207 rdata->pkt_info.src_name,
208 rdata->pkt_info.src_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000209 (int)rdata->msg_info.len,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000210 rdata->msg_info.msg_buf));
211
212 /* Always return false, otherwise messages will not get processed! */
213 return PJ_FALSE;
214}
Benny Prijonodc39fe82006-05-26 12:17:46 +0000215
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000216/* Notification on outgoing messages */
217static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
218{
219
220 /* Important note:
221 * tp_info field is only valid after outgoing messages has passed
222 * transport layer. So don't try to access tp_info when the module
223 * has lower priority than transport layer.
224 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000225
Benny Prijonob4a17c92006-07-10 14:40:21 +0000226 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
227 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000228 "--end msg--",
229 (tdata->buf.cur - tdata->buf.start),
230 pjsip_tx_data_get_info(tdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000231 tdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000232 tdata->tp_info.dst_name,
233 tdata->tp_info.dst_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000234 (int)(tdata->buf.cur - tdata->buf.start),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000235 tdata->buf.start));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000236
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000237 /* Always return success, otherwise message will not get sent! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000238 return PJ_SUCCESS;
239}
240
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000241/* The module instance. */
242static pjsip_module pjsua_msg_logger =
243{
244 NULL, NULL, /* prev, next. */
245 { "mod-pjsua-log", 13 }, /* Name. */
246 -1, /* Id */
247 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
248 NULL, /* load() */
249 NULL, /* start() */
250 NULL, /* stop() */
251 NULL, /* unload() */
252 &logging_on_rx_msg, /* on_rx_request() */
253 &logging_on_rx_msg, /* on_rx_response() */
254 &logging_on_tx_msg, /* on_tx_request. */
255 &logging_on_tx_msg, /* on_tx_response() */
256 NULL, /* on_tsx_state() */
257
258};
259
260
261/*****************************************************************************
Benny Prijono56315612006-07-18 14:39:40 +0000262 * Another simple module to handle incoming OPTIONS request
263 */
264
265/* Notification on incoming request */
266static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
267{
268 pjsip_tx_data *tdata;
269 pjsip_response_addr res_addr;
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000270 pjmedia_transport_info tpinfo;
Benny Prijono56315612006-07-18 14:39:40 +0000271 pjmedia_sdp_session *sdp;
272 const pjsip_hdr *cap_hdr;
273 pj_status_t status;
274
275 /* Only want to handle OPTIONS requests */
276 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000277 pjsip_get_options_method()) != 0)
Benny Prijono56315612006-07-18 14:39:40 +0000278 {
279 return PJ_FALSE;
280 }
281
282 /* Create basic response. */
283 status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL,
284 &tdata);
285 if (status != PJ_SUCCESS) {
286 pjsua_perror(THIS_FILE, "Unable to create OPTIONS response", status);
287 return PJ_TRUE;
288 }
289
290 /* Add Allow header */
291 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ALLOW, NULL);
292 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000293 pjsip_msg_add_hdr(tdata->msg,
294 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000295 }
296
297 /* Add Accept header */
298 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL);
299 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000300 pjsip_msg_add_hdr(tdata->msg,
301 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000302 }
303
304 /* Add Supported header */
305 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_SUPPORTED, NULL);
306 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000307 pjsip_msg_add_hdr(tdata->msg,
308 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000309 }
310
311 /* Add Allow-Events header from the evsub module */
312 cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL);
313 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000314 pjsip_msg_add_hdr(tdata->msg,
315 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000316 }
317
318 /* Add User-Agent header */
319 if (pjsua_var.ua_cfg.user_agent.slen) {
320 const pj_str_t USER_AGENT = { "User-Agent", 10};
321 pjsip_hdr *h;
322
323 h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
324 &USER_AGENT,
325 &pjsua_var.ua_cfg.user_agent);
326 pjsip_msg_add_hdr(tdata->msg, h);
327 }
328
Benny Prijono617c5bc2007-04-02 19:51:21 +0000329 /* Get media socket info */
Benny Prijono734fc2d2008-03-17 16:05:35 +0000330 pjmedia_transport_info_init(&tpinfo);
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000331 pjmedia_transport_get_info(pjsua_var.calls[0].med_tp, &tpinfo);
Benny Prijono617c5bc2007-04-02 19:51:21 +0000332
Benny Prijono56315612006-07-18 14:39:40 +0000333 /* Add SDP body, using call0's RTP address */
334 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool, 1,
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000335 &tpinfo.sock_info, &sdp);
Benny Prijono56315612006-07-18 14:39:40 +0000336 if (status == PJ_SUCCESS) {
337 pjsip_create_sdp_body(tdata->pool, sdp, &tdata->msg->body);
338 }
339
340 /* Send response statelessly */
341 pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
342 status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, tdata, NULL, NULL);
343 if (status != PJ_SUCCESS)
344 pjsip_tx_data_dec_ref(tdata);
345
346 return PJ_TRUE;
347}
348
349
350/* The module instance. */
351static pjsip_module pjsua_options_handler =
352{
353 NULL, NULL, /* prev, next. */
354 { "mod-pjsua-options", 17 }, /* Name. */
355 -1, /* Id */
356 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
357 NULL, /* load() */
358 NULL, /* start() */
359 NULL, /* stop() */
360 NULL, /* unload() */
361 &options_on_rx_request, /* on_rx_request() */
362 NULL, /* on_rx_response() */
363 NULL, /* on_tx_request. */
364 NULL, /* on_tx_response() */
365 NULL, /* on_tsx_state() */
366
367};
368
369
370/*****************************************************************************
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000371 * These two functions are the main callbacks registered to PJSIP stack
372 * to receive SIP request and response messages that are outside any
373 * dialogs and any transactions.
374 */
Benny Prijono268ca612006-02-07 12:34:11 +0000375
376/*
377 * Handler for receiving incoming requests.
378 *
379 * This handler serves multiple purposes:
380 * - it receives requests outside dialogs.
381 * - it receives requests inside dialogs, when the requests are
382 * unhandled by other dialog usages. Example of these
383 * requests are: MESSAGE.
384 */
385static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
386{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000387 pj_bool_t processed = PJ_FALSE;
388
389 PJSUA_LOCK();
Benny Prijono38998232006-02-08 22:44:25 +0000390
Benny Prijono84126ab2006-02-09 09:30:09 +0000391 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
Benny Prijono38998232006-02-08 22:44:25 +0000392
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000393 processed = pjsua_call_on_incoming(rdata);
Benny Prijono38998232006-02-08 22:44:25 +0000394 }
395
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000396 PJSUA_UNLOCK();
397
398 return processed;
Benny Prijono268ca612006-02-07 12:34:11 +0000399}
400
401
402/*
403 * Handler for receiving incoming responses.
404 *
405 * This handler serves multiple purposes:
406 * - it receives strayed responses (i.e. outside any dialog and
407 * outside any transactions).
408 * - it receives responses coming to a transaction, when pjsua
409 * module is set as transaction user for the transaction.
410 * - it receives responses inside a dialog, when these responses
411 * are unhandled by other dialog usages.
412 */
413static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
414{
415 PJ_UNUSED_ARG(rdata);
Benny Prijono268ca612006-02-07 12:34:11 +0000416 return PJ_FALSE;
417}
418
419
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000420/*****************************************************************************
421 * Logging.
422 */
423
424/* Log callback */
425static void log_writer(int level, const char *buffer, int len)
Benny Prijono268ca612006-02-07 12:34:11 +0000426{
Benny Prijono572d4852006-11-23 21:50:02 +0000427 /* Write to file, stdout or application callback. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000428
429 if (pjsua_var.log_file) {
430 pj_ssize_t size = len;
431 pj_file_write(pjsua_var.log_file, buffer, &size);
Benny Prijono980ca0a2007-04-03 17:55:08 +0000432 /* This will slow things down considerably! Don't do it!
433 pj_file_flush(pjsua_var.log_file);
434 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000435 }
436
Benny Prijono572d4852006-11-23 21:50:02 +0000437 if (level <= (int)pjsua_var.log_cfg.console_level) {
438 if (pjsua_var.log_cfg.cb)
439 (*pjsua_var.log_cfg.cb)(level, buffer, len);
440 else
441 pj_log_write(level, buffer, len);
442 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000443}
444
445
446/*
447 * Application can call this function at any time (after pjsua_create(), of
448 * course) to change logging settings.
449 */
450PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg)
451{
452 pj_status_t status;
453
454 /* Save config. */
455 pjsua_logging_config_dup(pjsua_var.pool, &pjsua_var.log_cfg, cfg);
456
457 /* Redirect log function to ours */
458 pj_log_set_log_func( &log_writer );
459
Benny Prijono9cb09a22007-08-30 09:35:10 +0000460 /* Set decor */
461 pj_log_set_decor(pjsua_var.log_cfg.decor);
462
Benny Prijono4190cf92008-01-18 13:25:05 +0000463 /* Set log level */
464 pj_log_set_level(pjsua_var.log_cfg.level);
465
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000466 /* Close existing file, if any */
467 if (pjsua_var.log_file) {
468 pj_file_close(pjsua_var.log_file);
469 pjsua_var.log_file = NULL;
470 }
471
472 /* If output log file is desired, create the file: */
473 if (pjsua_var.log_cfg.log_filename.slen) {
474
475 status = pj_file_open(pjsua_var.pool,
476 pjsua_var.log_cfg.log_filename.ptr,
477 PJ_O_WRONLY,
478 &pjsua_var.log_file);
479
480 if (status != PJ_SUCCESS) {
481 pjsua_perror(THIS_FILE, "Error creating log file", status);
482 return status;
483 }
484 }
485
486 /* Unregister msg logging if it's previously registered */
487 if (pjsua_msg_logger.id >= 0) {
488 pjsip_endpt_unregister_module(pjsua_var.endpt, &pjsua_msg_logger);
489 pjsua_msg_logger.id = -1;
490 }
491
492 /* Enable SIP message logging */
493 if (pjsua_var.log_cfg.msg_logging)
494 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_msg_logger);
495
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000496 return PJ_SUCCESS;
497}
498
499
500/*****************************************************************************
501 * PJSUA Base API.
502 */
503
504/* Worker thread function. */
505static int worker_thread(void *arg)
506{
507 enum { TIMEOUT = 10 };
Benny Prijonof8baa872006-02-27 23:54:23 +0000508
Benny Prijono268ca612006-02-07 12:34:11 +0000509 PJ_UNUSED_ARG(arg);
510
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000511 while (!pjsua_var.thread_quit_flag) {
512 int count;
513
514 count = pjsua_handle_events(TIMEOUT);
515 if (count < 0)
516 pj_thread_sleep(TIMEOUT);
517 }
Benny Prijono268ca612006-02-07 12:34:11 +0000518
519 return 0;
520}
521
Benny Prijonodc39fe82006-05-26 12:17:46 +0000522
Benny Prijono8389c312008-02-21 21:36:34 +0000523/* Init random seed */
524static void init_random_seed(void)
525{
526 pj_sockaddr addr;
527 const pj_str_t *hostname;
528 pj_uint32_t pid;
529 pj_time_val t;
530 unsigned seed=0;
531
532 /* Add hostname */
533 hostname = pj_gethostname();
534 seed = pj_hash_calc(seed, hostname->ptr, (int)hostname->slen);
535
536 /* Add primary IP address */
537 if (pj_gethostip(pj_AF_INET(), &addr)==PJ_SUCCESS)
538 seed = pj_hash_calc(seed, &addr.ipv4.sin_addr, 4);
539
540 /* Get timeofday */
541 pj_gettimeofday(&t);
542 seed = pj_hash_calc(seed, &t, sizeof(t));
543
544 /* Add PID */
545 pid = pj_getpid();
546 seed = pj_hash_calc(seed, &pid, sizeof(pid));
547
548 /* Init random seed */
549 pj_srand(seed);
550}
551
Benny Prijono268ca612006-02-07 12:34:11 +0000552/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000553 * Instantiate pjsua application.
Benny Prijonodc39fe82006-05-26 12:17:46 +0000554 */
555PJ_DEF(pj_status_t) pjsua_create(void)
556{
557 pj_status_t status;
Benny Prijono268ca612006-02-07 12:34:11 +0000558
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000559 /* Init pjsua data */
560 init_data();
Benny Prijono268ca612006-02-07 12:34:11 +0000561
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000562 /* Set default logging settings */
563 pjsua_logging_config_default(&pjsua_var.log_cfg);
564
565 /* Init PJLIB: */
Benny Prijono268ca612006-02-07 12:34:11 +0000566 status = pj_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000567 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
568
Benny Prijono8389c312008-02-21 21:36:34 +0000569 /* Init random seed */
570 init_random_seed();
Benny Prijono268ca612006-02-07 12:34:11 +0000571
Benny Prijonofccab712006-02-22 22:23:22 +0000572 /* Init PJLIB-UTIL: */
Benny Prijonofccab712006-02-22 22:23:22 +0000573 status = pjlib_util_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000574 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonofccab712006-02-22 22:23:22 +0000575
Benny Prijonoec921342007-03-24 13:00:30 +0000576 /* Init PJNATH */
577 status = pjnath_init();
578 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono268ca612006-02-07 12:34:11 +0000579
Benny Prijono094d3ad2006-11-21 08:41:00 +0000580 /* Set default sound device ID */
581 pjsua_var.cap_dev = pjsua_var.play_dev = -1;
582
Benny Prijono268ca612006-02-07 12:34:11 +0000583 /* Init caching pool. */
Benny Prijono106f5b72007-08-30 13:49:33 +0000584 pj_caching_pool_init(&pjsua_var.cp, NULL, 0);
Benny Prijono268ca612006-02-07 12:34:11 +0000585
586 /* Create memory pool for application. */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000587 pjsua_var.pool = pjsua_pool_create("pjsua", 1000, 1000);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000588
589 PJ_ASSERT_RETURN(pjsua_var.pool, PJ_ENOMEM);
Benny Prijono268ca612006-02-07 12:34:11 +0000590
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000591 /* Create mutex */
592 status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua",
593 &pjsua_var.mutex);
Benny Prijonoccf95622006-02-07 18:48:01 +0000594 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000595 pjsua_perror(THIS_FILE, "Unable to create mutex", status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000596 return status;
597 }
598
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000599 /* Must create SIP endpoint to initialize SIP parser. The parser
600 * is needed for example when application needs to call pjsua_verify_url().
Benny Prijonob8c25182006-04-29 08:31:09 +0000601 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000602 status = pjsip_endpt_create(&pjsua_var.cp.factory,
603 pj_gethostname()->ptr,
604 &pjsua_var.endpt);
605 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono39879152006-02-23 02:09:10 +0000606
607
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000608 return PJ_SUCCESS;
609}
610
611
612/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000613 * Initialize pjsua with the specified settings. All the settings are
614 * optional, and the default values will be used when the config is not
615 * specified.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000616 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000617PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
618 const pjsua_logging_config *log_cfg,
619 const pjsua_media_config *media_cfg)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000620{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000621 pjsua_config default_cfg;
622 pjsua_media_config default_media_cfg;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000623 const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
Benny Prijonodc39fe82006-05-26 12:17:46 +0000624 pj_status_t status;
625
626
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000627 /* Create default configurations when the config is not supplied */
628
629 if (ua_cfg == NULL) {
630 pjsua_config_default(&default_cfg);
631 ua_cfg = &default_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000632 }
633
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000634 if (media_cfg == NULL) {
635 pjsua_media_config_default(&default_media_cfg);
636 media_cfg = &default_media_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000637 }
638
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000639 /* Initialize logging first so that info/errors can be captured */
640 if (log_cfg) {
641 status = pjsua_reconfigure_logging(log_cfg);
642 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000643 }
644
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000645 /* If nameserver is configured, create DNS resolver instance and
646 * set it to be used by SIP resolver.
647 */
648 if (ua_cfg->nameserver_count) {
649#if PJSIP_HAS_RESOLVER
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000650 unsigned i;
651
652 /* Create DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000653 status = pjsip_endpt_create_resolver(pjsua_var.endpt,
654 &pjsua_var.resolver);
Benny Prijono318f3842007-05-15 20:27:08 +0000655 if (status != PJ_SUCCESS) {
656 pjsua_perror(THIS_FILE, "Error creating resolver", status);
657 return status;
658 }
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000659
660 /* Configure nameserver for the DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000661 status = pj_dns_resolver_set_ns(pjsua_var.resolver,
662 ua_cfg->nameserver_count,
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000663 ua_cfg->nameserver, NULL);
664 if (status != PJ_SUCCESS) {
665 pjsua_perror(THIS_FILE, "Error setting nameserver", status);
666 return status;
667 }
668
669 /* Set this DNS resolver to be used by the SIP resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000670 status = pjsip_endpt_set_resolver(pjsua_var.endpt, pjsua_var.resolver);
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000671 if (status != PJ_SUCCESS) {
672 pjsua_perror(THIS_FILE, "Error setting DNS resolver", status);
673 return status;
674 }
675
676 /* Print nameservers */
677 for (i=0; i<ua_cfg->nameserver_count; ++i) {
678 PJ_LOG(4,(THIS_FILE, "Nameserver %.*s added",
679 (int)ua_cfg->nameserver[i].slen,
680 ua_cfg->nameserver[i].ptr));
681 }
682#else
683 PJ_LOG(2,(THIS_FILE,
684 "DNS resolver is disabled (PJSIP_HAS_RESOLVER==0)"));
685#endif
686 }
687
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000688 /* Init SIP UA: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000689
690 /* Initialize transaction layer: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000691 status = pjsip_tsx_layer_init_module(pjsua_var.endpt);
692 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000693
Benny Prijonodc39fe82006-05-26 12:17:46 +0000694
695 /* Initialize UA layer module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000696 status = pjsip_ua_init_module( pjsua_var.endpt, NULL );
697 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000698
Benny Prijonodc39fe82006-05-26 12:17:46 +0000699
Benny Prijono053f5222006-11-11 16:16:04 +0000700 /* Initialize Replaces support. */
701 status = pjsip_replaces_init_module( pjsua_var.endpt );
702 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
703
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000704 /* Initialize 100rel support */
705 status = pjsip_100rel_init_module(pjsua_var.endpt);
706 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono053f5222006-11-11 16:16:04 +0000707
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000708 /* Initialize and register PJSUA application module. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000709 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000710 const pjsip_module mod_initializer =
Benny Prijonodc39fe82006-05-26 12:17:46 +0000711 {
712 NULL, NULL, /* prev, next. */
713 { "mod-pjsua", 9 }, /* Name. */
714 -1, /* Id */
715 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
716 NULL, /* load() */
717 NULL, /* start() */
718 NULL, /* stop() */
719 NULL, /* unload() */
720 &mod_pjsua_on_rx_request, /* on_rx_request() */
721 &mod_pjsua_on_rx_response, /* on_rx_response() */
722 NULL, /* on_tx_request. */
723 NULL, /* on_tx_response() */
724 NULL, /* on_tsx_state() */
725 };
726
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000727 pjsua_var.mod = mod_initializer;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000728
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000729 status = pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_var.mod);
730 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000731 }
732
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000733
Benny Prijonodc39fe82006-05-26 12:17:46 +0000734
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000735 /* Initialize PJSUA call subsystem: */
736 status = pjsua_call_subsys_init(ua_cfg);
737 if (status != PJ_SUCCESS)
Benny Prijonodc39fe82006-05-26 12:17:46 +0000738 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000739
740
Benny Prijonoc97608e2007-03-23 16:34:20 +0000741 /* Start resolving STUN server */
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000742
Benny Prijonoc97608e2007-03-23 16:34:20 +0000743 status = pjsua_resolve_stun_server(PJ_FALSE);
744 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
745 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
746 return status;
747 }
748
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000749 /* Initialize PJSUA media subsystem */
750 status = pjsua_media_subsys_init(media_cfg);
751 if (status != PJ_SUCCESS)
752 goto on_error;
753
Benny Prijonodc39fe82006-05-26 12:17:46 +0000754
755 /* Init core SIMPLE module : */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000756 status = pjsip_evsub_init_module(pjsua_var.endpt);
757 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000758
Benny Prijonodc39fe82006-05-26 12:17:46 +0000759
760 /* Init presence module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000761 status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance());
762 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000763
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000764 /* Init PUBLISH module */
765 pjsip_publishc_init_module(pjsua_var.endpt);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000766
767 /* Init xfer/REFER module */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000768 status = pjsip_xfer_init_module( pjsua_var.endpt );
769 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000770
771 /* Init pjsua presence handler: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000772 status = pjsua_pres_init();
773 if (status != PJ_SUCCESS)
774 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000775
776 /* Init out-of-dialog MESSAGE request handler. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000777 status = pjsua_im_init();
778 if (status != PJ_SUCCESS)
779 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000780
Benny Prijonoc8141a82006-08-20 09:12:19 +0000781 /* Register OPTIONS handler */
782 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_options_handler);
783
784 /* Add OPTIONS in Allow header */
785 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_ALLOW,
786 NULL, 1, &STR_OPTIONS);
787
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000788 /* Start worker thread if needed. */
789 if (pjsua_var.ua_cfg.thread_cnt) {
790 unsigned i;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000791
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000792 if (pjsua_var.ua_cfg.thread_cnt > PJ_ARRAY_SIZE(pjsua_var.thread))
793 pjsua_var.ua_cfg.thread_cnt = PJ_ARRAY_SIZE(pjsua_var.thread);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000794
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000795 for (i=0; i<pjsua_var.ua_cfg.thread_cnt; ++i) {
796 status = pj_thread_create(pjsua_var.pool, "pjsua", &worker_thread,
797 NULL, 0, 0, &pjsua_var.thread[i]);
798 if (status != PJ_SUCCESS)
799 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000800 }
Benny Prijonof521eb02006-08-06 23:07:25 +0000801 PJ_LOG(4,(THIS_FILE, "%d SIP worker threads created",
802 pjsua_var.ua_cfg.thread_cnt));
803 } else {
804 PJ_LOG(4,(THIS_FILE, "No SIP worker threads created"));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000805 }
806
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000807 /* Done! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000808
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000809 PJ_LOG(3,(THIS_FILE, "pjsua version %s for %s initialized",
Benny Prijono106f5b72007-08-30 13:49:33 +0000810 pj_get_version(), PJ_OS_NAME));
Benny Prijonoccf95622006-02-07 18:48:01 +0000811
Benny Prijono268ca612006-02-07 12:34:11 +0000812 return PJ_SUCCESS;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000813
814on_error:
815 pjsua_destroy();
816 return status;
Benny Prijono268ca612006-02-07 12:34:11 +0000817}
818
819
Benny Prijono834aee32006-02-19 01:38:06 +0000820/* Sleep with polling */
821static void busy_sleep(unsigned msec)
822{
Benny Prijonob2c96822007-05-03 13:31:21 +0000823#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
Benny Prijono897f9f82007-05-03 19:56:21 +0000824 /* Ideally we shouldn't call pj_thread_sleep() and rather
825 * CActiveScheduler::WaitForAnyRequest() here, but that will
826 * drag in Symbian header and it doesn't look pretty.
827 */
Benny Prijonob2c96822007-05-03 13:31:21 +0000828 pj_thread_sleep(msec);
829#else
Benny Prijono834aee32006-02-19 01:38:06 +0000830 pj_time_val timeout, now;
831
832 pj_gettimeofday(&timeout);
833 timeout.msec += msec;
834 pj_time_val_normalize(&timeout);
835
836 do {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000837 while (pjsua_handle_events(10) > 0)
838 ;
Benny Prijono834aee32006-02-19 01:38:06 +0000839 pj_gettimeofday(&now);
840 } while (PJ_TIME_VAL_LT(now, timeout));
Benny Prijonob2c96822007-05-03 13:31:21 +0000841#endif
Benny Prijono834aee32006-02-19 01:38:06 +0000842}
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
1199 /* Ideally we shouldn't call pj_thread_sleep() and rather
1200 * CActiveScheduler::WaitForAnyRequest() here, but that will
1201 * drag in Symbian header and it doesn't look pretty.
1202 */
1203 pj_thread_sleep(msec_timeout);
1204 return msec_timeout;
1205#else
1206
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001207 unsigned count = 0;
1208 pj_time_val tv;
1209 pj_status_t status;
1210
1211 tv.sec = 0;
1212 tv.msec = msec_timeout;
1213 pj_time_val_normalize(&tv);
1214
1215 status = pjsip_endpt_handle_events2(pjsua_var.endpt, &tv, &count);
1216
1217 if (status != PJ_SUCCESS)
1218 return -status;
1219
1220 return count;
Benny Prijono897f9f82007-05-03 19:56:21 +00001221#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001222}
1223
1224
1225/*
1226 * Create memory pool.
1227 */
1228PJ_DEF(pj_pool_t*) pjsua_pool_create( const char *name, pj_size_t init_size,
1229 pj_size_t increment)
1230{
1231 /* Pool factory is thread safe, no need to lock */
1232 return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment,
1233 NULL);
1234}
1235
1236
1237/*
1238 * Internal function to get SIP endpoint instance of pjsua, which is
1239 * needed for example to register module, create transports, etc.
1240 * Probably is only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001241 */
1242PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void)
1243{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001244 return pjsua_var.endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001245}
1246
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001247/*
1248 * Internal function to get media endpoint instance.
1249 * Only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001250 */
1251PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void)
1252{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001253 return pjsua_var.med_endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001254}
1255
Benny Prijono97b87172006-08-24 14:25:14 +00001256/*
1257 * Internal function to get PJSUA pool factory.
1258 */
1259PJ_DEF(pj_pool_factory*) pjsua_get_pool_factory(void)
1260{
1261 return &pjsua_var.cp.factory;
1262}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001263
1264/*****************************************************************************
1265 * PJSUA SIP Transport API.
1266 */
1267
1268/*
Benny Prijono23674a32007-12-01 08:59:25 +00001269 * Tools to get address string.
1270 */
1271static const char *addr_string(const pj_sockaddr_t *addr)
1272{
1273 static char str[128];
1274 str[0] = '\0';
1275 pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family,
1276 pj_sockaddr_get_addr(addr),
1277 str, sizeof(str));
1278 return str;
1279}
1280
1281/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001282 * Create and initialize SIP socket (and possibly resolve public
1283 * address via STUN, depending on config).
1284 */
Benny Prijono23674a32007-12-01 08:59:25 +00001285static pj_status_t create_sip_udp_sock(int af,
1286 const pj_str_t *bind_param,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001287 int port,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001288 pj_sock_t *p_sock,
Benny Prijono23674a32007-12-01 08:59:25 +00001289 pj_sockaddr *p_pub_addr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001290{
Benny Prijono23674a32007-12-01 08:59:25 +00001291 char stun_ip_addr[PJ_INET6_ADDRSTRLEN];
Benny Prijonoc97608e2007-03-23 16:34:20 +00001292 pj_str_t stun_srv;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001293 pj_sock_t sock;
Benny Prijono23674a32007-12-01 08:59:25 +00001294 pj_sockaddr bind_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001295 pj_status_t status;
1296
Benny Prijonoc97608e2007-03-23 16:34:20 +00001297 /* Make sure STUN server resolution has completed */
1298 status = pjsua_resolve_stun_server(PJ_TRUE);
1299 if (status != PJ_SUCCESS) {
1300 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
1301 return status;
1302 }
1303
Benny Prijono23674a32007-12-01 08:59:25 +00001304 /* Initialize bound address */
1305 if (bind_param->slen) {
1306 status = pj_sockaddr_init(af, &bind_addr, bind_param,
1307 (pj_uint16_t)port);
1308 if (status != PJ_SUCCESS) {
1309 pjsua_perror(THIS_FILE,
1310 "Unable to resolve transport bound address",
1311 status);
1312 return status;
1313 }
1314 } else {
1315 pj_sockaddr_init(af, &bind_addr, NULL, (pj_uint16_t)port);
1316 }
1317
1318 status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001319 if (status != PJ_SUCCESS) {
1320 pjsua_perror(THIS_FILE, "socket() error", status);
Benny Prijonod8410532006-06-15 11:04:33 +00001321 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001322 }
1323
Benny Prijono5f55a1f2007-12-05 05:08:29 +00001324 status = pj_sock_bind(sock, &bind_addr, pj_sockaddr_get_len(&bind_addr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001325 if (status != PJ_SUCCESS) {
1326 pjsua_perror(THIS_FILE, "bind() error", status);
1327 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001328 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001329 }
1330
Benny Prijonoe347cb02007-02-14 14:36:13 +00001331 /* If port is zero, get the bound port */
1332 if (port == 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001333 pj_sockaddr bound_addr;
Benny Prijonoe347cb02007-02-14 14:36:13 +00001334 int namelen = sizeof(bound_addr);
1335 status = pj_sock_getsockname(sock, &bound_addr, &namelen);
1336 if (status != PJ_SUCCESS) {
1337 pjsua_perror(THIS_FILE, "getsockname() error", status);
1338 pj_sock_close(sock);
1339 return status;
1340 }
1341
Benny Prijono23674a32007-12-01 08:59:25 +00001342 port = pj_sockaddr_get_port(&bound_addr);
Benny Prijonoe347cb02007-02-14 14:36:13 +00001343 }
1344
Benny Prijonoc97608e2007-03-23 16:34:20 +00001345 if (pjsua_var.stun_srv.addr.sa_family != 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001346 pj_ansi_strcpy(stun_ip_addr,pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
1347 stun_srv = pj_str(stun_ip_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001348 } else {
Benny Prijonoc97608e2007-03-23 16:34:20 +00001349 stun_srv.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001350 }
1351
1352 /* Get the published address, either by STUN or by resolving
1353 * the name of local host.
1354 */
Benny Prijono23674a32007-12-01 08:59:25 +00001355 if (pj_sockaddr_has_addr(p_pub_addr)) {
Benny Prijono744aca02007-11-11 03:06:07 +00001356 /*
1357 * Public address is already specified, no need to resolve the
1358 * address, only set the port.
1359 */
Benny Prijono23674a32007-12-01 08:59:25 +00001360 if (pj_sockaddr_get_port(p_pub_addr) == 0)
1361 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijono744aca02007-11-11 03:06:07 +00001362
1363 } else if (stun_srv.slen) {
Benny Prijono0a5cad82006-09-26 13:21:02 +00001364 /*
1365 * STUN is specified, resolve the address with STUN.
1366 */
Benny Prijono23674a32007-12-01 08:59:25 +00001367 if (af != pj_AF_INET()) {
1368 pjsua_perror(THIS_FILE, "Cannot use STUN", PJ_EAFNOTSUP);
1369 pj_sock_close(sock);
1370 return PJ_EAFNOTSUP;
1371 }
1372
Benny Prijono14c2b862007-02-21 00:40:05 +00001373 status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001374 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
1375 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
Benny Prijono23674a32007-12-01 08:59:25 +00001376 &p_pub_addr->ipv4);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001377 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +00001378 pjsua_perror(THIS_FILE, "Error contacting STUN server", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001379 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001380 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001381 }
1382
1383 } else {
Benny Prijono23674a32007-12-01 08:59:25 +00001384 pj_bzero(p_pub_addr, sizeof(pj_sockaddr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001385
Benny Prijono42d08d22007-12-20 11:23:07 +00001386 if (pj_sockaddr_has_addr(&bind_addr)) {
1387 pj_sockaddr_copy_addr(p_pub_addr, &bind_addr);
1388 } else {
1389 status = pj_gethostip(af, p_pub_addr);
1390 if (status != PJ_SUCCESS) {
1391 pjsua_perror(THIS_FILE, "Unable to get local host IP", status);
1392 pj_sock_close(sock);
1393 return status;
1394 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001395 }
1396
Benny Prijono23674a32007-12-01 08:59:25 +00001397 p_pub_addr->addr.sa_family = (pj_uint16_t)af;
1398 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001399 }
1400
1401 *p_sock = sock;
1402
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001403 PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
Benny Prijono23674a32007-12-01 08:59:25 +00001404 addr_string(p_pub_addr),
1405 (int)pj_sockaddr_get_port(p_pub_addr)));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001406
Benny Prijonod8410532006-06-15 11:04:33 +00001407 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001408}
1409
1410
1411/*
1412 * Create SIP transport.
1413 */
1414PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
1415 const pjsua_transport_config *cfg,
1416 pjsua_transport_id *p_id)
1417{
1418 pjsip_transport *tp;
1419 unsigned id;
1420 pj_status_t status;
1421
1422 PJSUA_LOCK();
1423
1424 /* Find empty transport slot */
1425 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001426 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001427 break;
1428 }
1429
1430 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1431 status = PJ_ETOOMANY;
1432 pjsua_perror(THIS_FILE, "Error creating transport", status);
1433 goto on_return;
1434 }
1435
1436 /* Create the transport */
Benny Prijonob2477142007-12-05 04:09:59 +00001437 if (type==PJSIP_TRANSPORT_UDP || type==PJSIP_TRANSPORT_UDP6) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001438 /*
Benny Prijono23674a32007-12-01 08:59:25 +00001439 * Create UDP transport (IPv4 or IPv6).
Benny Prijonoe93e2872006-06-28 16:46:49 +00001440 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001441 pjsua_transport_config config;
Benny Prijono23674a32007-12-01 08:59:25 +00001442 char hostbuf[PJ_INET6_ADDRSTRLEN];
Benny Prijonod8410532006-06-15 11:04:33 +00001443 pj_sock_t sock = PJ_INVALID_SOCKET;
Benny Prijono23674a32007-12-01 08:59:25 +00001444 pj_sockaddr pub_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001445 pjsip_host_port addr_name;
1446
1447 /* Supply default config if it's not specified */
1448 if (cfg == NULL) {
1449 pjsua_transport_config_default(&config);
1450 cfg = &config;
1451 }
1452
Benny Prijono0a5cad82006-09-26 13:21:02 +00001453 /* Initialize the public address from the config, if any */
Benny Prijono23674a32007-12-01 08:59:25 +00001454 pj_sockaddr_init(pjsip_transport_type_get_af(type), &pub_addr,
1455 NULL, (pj_uint16_t)cfg->port);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001456 if (cfg->public_addr.slen) {
Benny Prijono23674a32007-12-01 08:59:25 +00001457 status = pj_sockaddr_set_str_addr(pjsip_transport_type_get_af(type),
1458 &pub_addr, &cfg->public_addr);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001459 if (status != PJ_SUCCESS) {
1460 pjsua_perror(THIS_FILE,
1461 "Unable to resolve transport public address",
1462 status);
1463 goto on_return;
1464 }
1465 }
1466
1467 /* Create the socket and possibly resolve the address with STUN
1468 * (only when public address is not specified).
1469 */
Benny Prijono23674a32007-12-01 08:59:25 +00001470 status = create_sip_udp_sock(pjsip_transport_type_get_af(type),
1471 &cfg->bound_addr, cfg->port,
Benny Prijono0a5cad82006-09-26 13:21:02 +00001472 &sock, &pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001473 if (status != PJ_SUCCESS)
1474 goto on_return;
1475
Benny Prijono23674a32007-12-01 08:59:25 +00001476 pj_ansi_strcpy(hostbuf, addr_string(&pub_addr));
1477 addr_name.host = pj_str(hostbuf);
1478 addr_name.port = pj_sockaddr_get_port(&pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001479
1480 /* Create UDP transport */
Benny Prijono23674a32007-12-01 08:59:25 +00001481 status = pjsip_udp_transport_attach2(pjsua_var.endpt, type, sock,
1482 &addr_name, 1, &tp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001483 if (status != PJ_SUCCESS) {
1484 pjsua_perror(THIS_FILE, "Error creating SIP UDP transport",
1485 status);
1486 pj_sock_close(sock);
1487 goto on_return;
1488 }
1489
Benny Prijonoe93e2872006-06-28 16:46:49 +00001490
1491 /* Save the transport */
1492 pjsua_var.tpdata[id].type = type;
1493 pjsua_var.tpdata[id].local_name = tp->local_name;
1494 pjsua_var.tpdata[id].data.tp = tp;
1495
Benny Prijono3569c0d2007-04-06 10:29:20 +00001496#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
1497
Benny Prijonob2477142007-12-05 04:09:59 +00001498 } else if (type == PJSIP_TRANSPORT_TCP || type == PJSIP_TRANSPORT_TCP6) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001499 /*
1500 * Create TCP transport.
1501 */
1502 pjsua_transport_config config;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001503 pjsip_host_port a_name;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001504 pjsip_tpfactory *tcp;
1505 pj_sockaddr_in local_addr;
1506
1507 /* Supply default config if it's not specified */
1508 if (cfg == NULL) {
1509 pjsua_transport_config_default(&config);
1510 cfg = &config;
1511 }
1512
1513 /* Init local address */
1514 pj_sockaddr_in_init(&local_addr, 0, 0);
1515
1516 if (cfg->port)
1517 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1518
Benny Prijono0a5cad82006-09-26 13:21:02 +00001519 if (cfg->bound_addr.slen) {
1520 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1521 if (status != PJ_SUCCESS) {
1522 pjsua_perror(THIS_FILE,
1523 "Unable to resolve transport bound address",
1524 status);
1525 goto on_return;
1526 }
1527 }
1528
1529 /* Init published name */
1530 pj_bzero(&a_name, sizeof(pjsip_host_port));
1531 if (cfg->public_addr.slen)
1532 a_name.host = cfg->public_addr;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001533
1534 /* Create the TCP transport */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001535 status = pjsip_tcp_transport_start2(pjsua_var.endpt, &local_addr,
1536 &a_name, 1, &tcp);
Benny Prijonoe93e2872006-06-28 16:46:49 +00001537
1538 if (status != PJ_SUCCESS) {
1539 pjsua_perror(THIS_FILE, "Error creating SIP TCP listener",
1540 status);
1541 goto on_return;
1542 }
1543
1544 /* Save the transport */
1545 pjsua_var.tpdata[id].type = type;
1546 pjsua_var.tpdata[id].local_name = tcp->addr_name;
1547 pjsua_var.tpdata[id].data.factory = tcp;
1548
Benny Prijono3569c0d2007-04-06 10:29:20 +00001549#endif /* PJ_HAS_TCP */
1550
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001551#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
1552 } else if (type == PJSIP_TRANSPORT_TLS) {
1553 /*
1554 * Create TLS transport.
1555 */
Benny Prijonof3bbc132006-12-25 06:43:59 +00001556 /*
1557 * Create TCP transport.
1558 */
1559 pjsua_transport_config config;
1560 pjsip_host_port a_name;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001561 pjsip_tpfactory *tls;
Benny Prijonof3bbc132006-12-25 06:43:59 +00001562 pj_sockaddr_in local_addr;
1563
1564 /* Supply default config if it's not specified */
1565 if (cfg == NULL) {
1566 pjsua_transport_config_default(&config);
1567 config.port = 5061;
1568 cfg = &config;
1569 }
1570
1571 /* Init local address */
1572 pj_sockaddr_in_init(&local_addr, 0, 0);
1573
1574 if (cfg->port)
1575 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1576
1577 if (cfg->bound_addr.slen) {
1578 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1579 if (status != PJ_SUCCESS) {
1580 pjsua_perror(THIS_FILE,
1581 "Unable to resolve transport bound address",
1582 status);
1583 goto on_return;
1584 }
1585 }
1586
1587 /* Init published name */
1588 pj_bzero(&a_name, sizeof(pjsip_host_port));
1589 if (cfg->public_addr.slen)
1590 a_name.host = cfg->public_addr;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001591
1592 status = pjsip_tls_transport_start(pjsua_var.endpt,
Benny Prijonof3bbc132006-12-25 06:43:59 +00001593 &cfg->tls_setting,
1594 &local_addr, &a_name, 1, &tls);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001595 if (status != PJ_SUCCESS) {
1596 pjsua_perror(THIS_FILE, "Error creating SIP TLS listener",
1597 status);
1598 goto on_return;
1599 }
1600
1601 /* Save the transport */
1602 pjsua_var.tpdata[id].type = type;
1603 pjsua_var.tpdata[id].local_name = tls->addr_name;
1604 pjsua_var.tpdata[id].data.factory = tls;
1605#endif
1606
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001607 } else {
1608 status = PJSIP_EUNSUPTRANSPORT;
1609 pjsua_perror(THIS_FILE, "Error creating transport", status);
1610 goto on_return;
1611 }
1612
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001613
1614 /* Return the ID */
1615 if (p_id) *p_id = id;
1616
1617 status = PJ_SUCCESS;
1618
1619on_return:
1620
1621 PJSUA_UNLOCK();
1622
Benny Prijonod8410532006-06-15 11:04:33 +00001623 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001624}
1625
1626
1627/*
1628 * Register transport that has been created by application.
1629 */
1630PJ_DEF(pj_status_t) pjsua_transport_register( pjsip_transport *tp,
1631 pjsua_transport_id *p_id)
1632{
1633 unsigned id;
1634
1635 PJSUA_LOCK();
1636
1637 /* Find empty transport slot */
1638 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001639 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001640 break;
1641 }
1642
1643 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1644 pjsua_perror(THIS_FILE, "Error creating transport", PJ_ETOOMANY);
1645 PJSUA_UNLOCK();
1646 return PJ_ETOOMANY;
1647 }
1648
1649 /* Save the transport */
Benny Prijonod17a5e92007-05-29 11:51:45 +00001650 pjsua_var.tpdata[id].type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001651 pjsua_var.tpdata[id].local_name = tp->local_name;
1652 pjsua_var.tpdata[id].data.tp = tp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001653
1654 /* Return the ID */
1655 if (p_id) *p_id = id;
1656
1657 PJSUA_UNLOCK();
1658
1659 return PJ_SUCCESS;
1660}
1661
1662
1663/*
1664 * Enumerate all transports currently created in the system.
1665 */
1666PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[],
1667 unsigned *p_count )
1668{
1669 unsigned i, count;
1670
1671 PJSUA_LOCK();
1672
1673 for (i=0, count=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata) && count<*p_count;
1674 ++i)
1675 {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001676 if (!pjsua_var.tpdata[i].data.ptr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001677 continue;
1678
1679 id[count++] = i;
1680 }
1681
1682 *p_count = count;
1683
1684 PJSUA_UNLOCK();
1685
1686 return PJ_SUCCESS;
1687}
1688
1689
1690/*
1691 * Get information about transports.
1692 */
1693PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
1694 pjsua_transport_info *info)
1695{
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001696 pjsua_transport_data *t = &pjsua_var.tpdata[id];
Benny Prijono0a5cad82006-09-26 13:21:02 +00001697 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001698
Benny Prijonoac623b32006-07-03 15:19:31 +00001699 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001700
1701 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001702 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1703 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001704
1705 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001706 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001707
1708 PJSUA_LOCK();
1709
Benny Prijonoe93e2872006-06-28 16:46:49 +00001710 if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_UDP) {
1711
1712 pjsip_transport *tp = t->data.tp;
1713
1714 if (tp == NULL) {
1715 PJSUA_UNLOCK();
1716 return PJ_EINVALIDOP;
1717 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001718
Benny Prijonoe93e2872006-06-28 16:46:49 +00001719 info->id = id;
Benny Prijonod17a5e92007-05-29 11:51:45 +00001720 info->type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001721 info->type_name = pj_str(tp->type_name);
1722 info->info = pj_str(tp->info);
1723 info->flag = tp->flag;
1724 info->addr_len = tp->addr_len;
1725 info->local_addr = tp->local_addr;
1726 info->local_name = tp->local_name;
1727 info->usage_count = pj_atomic_get(tp->ref_cnt);
1728
Benny Prijono0a5cad82006-09-26 13:21:02 +00001729 status = PJ_SUCCESS;
1730
Benny Prijonoe93e2872006-06-28 16:46:49 +00001731 } else if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_TCP) {
1732
1733 pjsip_tpfactory *factory = t->data.factory;
1734
1735 if (factory == NULL) {
1736 PJSUA_UNLOCK();
1737 return PJ_EINVALIDOP;
1738 }
1739
1740 info->id = id;
1741 info->type = t->type;
1742 info->type_name = pj_str("TCP");
1743 info->info = pj_str("TCP transport");
1744 info->flag = factory->flag;
1745 info->addr_len = sizeof(factory->local_addr);
1746 info->local_addr = factory->local_addr;
1747 info->local_name = factory->addr_name;
1748 info->usage_count = 0;
1749
Benny Prijono0a5cad82006-09-26 13:21:02 +00001750 status = PJ_SUCCESS;
1751
1752 } else {
1753 pj_assert(!"Unsupported transport");
1754 status = PJ_EINVALIDOP;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001755 }
1756
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001757
1758 PJSUA_UNLOCK();
1759
Benny Prijono0a5cad82006-09-26 13:21:02 +00001760 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001761}
1762
1763
1764/*
1765 * Disable a transport or re-enable it.
1766 */
1767PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id,
1768 pj_bool_t enabled)
1769{
1770 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001771 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1772 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001773
1774 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001775 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001776
1777
1778 /* To be done!! */
1779 PJ_TODO(pjsua_transport_set_enable);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001780 PJ_UNUSED_ARG(enabled);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001781
1782 return PJ_EINVALIDOP;
1783}
1784
1785
1786/*
1787 * Close the transport.
1788 */
1789PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
1790 pj_bool_t force )
1791{
Benny Prijono5ff61872007-02-01 03:37:11 +00001792 pj_status_t status;
1793
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001794 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001795 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1796 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001797
1798 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001799 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001800
Benny Prijono0a5cad82006-09-26 13:21:02 +00001801 /* Note: destroy() may not work if there are objects still referencing
1802 * the transport.
1803 */
1804 if (force) {
1805 switch (pjsua_var.tpdata[id].type) {
1806 case PJSIP_TRANSPORT_UDP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001807 status = pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
1808 if (status != PJ_SUCCESS)
1809 return status;
1810 status = pjsip_transport_destroy(pjsua_var.tpdata[id].data.tp);
1811 if (status != PJ_SUCCESS)
1812 return status;
1813 break;
1814
1815 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001816 case PJSIP_TRANSPORT_TCP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001817 /* This will close the TCP listener, but existing TCP/TLS
1818 * connections (if any) will still linger
1819 */
1820 status = (*pjsua_var.tpdata[id].data.factory->destroy)
1821 (pjsua_var.tpdata[id].data.factory);
1822 if (status != PJ_SUCCESS)
1823 return status;
1824
Benny Prijono0a5cad82006-09-26 13:21:02 +00001825 break;
Benny Prijono5ff61872007-02-01 03:37:11 +00001826
Benny Prijono1ebd6142006-10-19 15:48:02 +00001827 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001828 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001829 }
1830
1831 } else {
Benny Prijono5ff61872007-02-01 03:37:11 +00001832 /* If force is not specified, transports will be closed at their
1833 * convenient time. However this will leak PJSUA-API transport
1834 * descriptors as PJSUA-API wouldn't know when exactly the
1835 * transport is closed thus it can't cleanup PJSUA transport
1836 * descriptor.
1837 */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001838 switch (pjsua_var.tpdata[id].type) {
1839 case PJSIP_TRANSPORT_UDP:
1840 return pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
Benny Prijono5ff61872007-02-01 03:37:11 +00001841 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001842 case PJSIP_TRANSPORT_TCP:
1843 return (*pjsua_var.tpdata[id].data.factory->destroy)
1844 (pjsua_var.tpdata[id].data.factory);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001845 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001846 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001847 }
1848 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001849
Benny Prijono5ff61872007-02-01 03:37:11 +00001850 /* Cleanup pjsua data when force is applied */
1851 if (force) {
1852 pjsua_var.tpdata[id].type = PJSIP_TRANSPORT_UNSPECIFIED;
1853 pjsua_var.tpdata[id].data.ptr = NULL;
1854 }
1855
1856 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001857}
1858
1859
1860/*
1861 * Add additional headers etc in msg_data specified by application
1862 * when sending requests.
1863 */
1864void pjsua_process_msg_data(pjsip_tx_data *tdata,
1865 const pjsua_msg_data *msg_data)
1866{
1867 pj_bool_t allow_body;
1868 const pjsip_hdr *hdr;
1869
Benny Prijono56315612006-07-18 14:39:40 +00001870 /* Always add User-Agent */
1871 if (pjsua_var.ua_cfg.user_agent.slen &&
1872 tdata->msg->type == PJSIP_REQUEST_MSG)
1873 {
1874 const pj_str_t STR_USER_AGENT = { "User-Agent", 10 };
1875 pjsip_hdr *h;
1876 h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool,
1877 &STR_USER_AGENT,
1878 &pjsua_var.ua_cfg.user_agent);
1879 pjsip_msg_add_hdr(tdata->msg, h);
1880 }
1881
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001882 if (!msg_data)
1883 return;
1884
1885 hdr = msg_data->hdr_list.next;
1886 while (hdr && hdr != &msg_data->hdr_list) {
1887 pjsip_hdr *new_hdr;
1888
Benny Prijonoa1e69682007-05-11 15:14:34 +00001889 new_hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001890 pjsip_msg_add_hdr(tdata->msg, new_hdr);
1891
1892 hdr = hdr->next;
1893 }
1894
1895 allow_body = (tdata->msg->body == NULL);
1896
1897 if (allow_body && msg_data->content_type.slen && msg_data->msg_body.slen) {
1898 pjsip_media_type ctype;
1899 pjsip_msg_body *body;
1900
1901 pjsua_parse_media_type(tdata->pool, &msg_data->content_type, &ctype);
1902 body = pjsip_msg_body_create(tdata->pool, &ctype.type, &ctype.subtype,
1903 &msg_data->msg_body);
1904 tdata->msg->body = body;
1905 }
1906}
1907
1908
1909/*
1910 * Add route_set to outgoing requests
1911 */
1912void pjsua_set_msg_route_set( pjsip_tx_data *tdata,
1913 const pjsip_route_hdr *route_set )
1914{
1915 const pjsip_route_hdr *r;
1916
1917 r = route_set->next;
1918 while (r != route_set) {
1919 pjsip_route_hdr *new_r;
1920
Benny Prijonoa1e69682007-05-11 15:14:34 +00001921 new_r = (pjsip_route_hdr*) pjsip_hdr_clone(tdata->pool, r);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001922 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)new_r);
1923
1924 r = r->next;
1925 }
1926}
1927
1928
1929/*
1930 * Simple version of MIME type parsing (it doesn't support parameters)
1931 */
1932void pjsua_parse_media_type( pj_pool_t *pool,
1933 const pj_str_t *mime,
1934 pjsip_media_type *media_type)
1935{
1936 pj_str_t tmp;
1937 char *pos;
1938
Benny Prijonoac623b32006-07-03 15:19:31 +00001939 pj_bzero(media_type, sizeof(*media_type));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001940
1941 pj_strdup_with_null(pool, &tmp, mime);
1942
1943 pos = pj_strchr(&tmp, '/');
1944 if (pos) {
1945 media_type->type.ptr = tmp.ptr;
1946 media_type->type.slen = (pos-tmp.ptr);
1947 media_type->subtype.ptr = pos+1;
1948 media_type->subtype.slen = tmp.ptr+tmp.slen-pos-1;
1949 } else {
1950 media_type->type = tmp;
1951 }
1952}
1953
1954
1955/*
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001956 * Internal function to init transport selector from transport id.
1957 */
1958void pjsua_init_tpselector(pjsua_transport_id tp_id,
1959 pjsip_tpselector *sel)
1960{
1961 pjsua_transport_data *tpdata;
1962 unsigned flag;
1963
1964 pj_bzero(sel, sizeof(*sel));
1965 if (tp_id == PJSUA_INVALID_ID)
1966 return;
1967
Benny Prijonoa1e69682007-05-11 15:14:34 +00001968 pj_assert(tp_id >= 0 && tp_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata));
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001969 tpdata = &pjsua_var.tpdata[tp_id];
1970
1971 flag = pjsip_transport_get_flag_from_type(tpdata->type);
1972
1973 if (flag & PJSIP_TRANSPORT_DATAGRAM) {
1974 sel->type = PJSIP_TPSELECTOR_TRANSPORT;
1975 sel->u.transport = tpdata->data.tp;
1976 } else {
1977 sel->type = PJSIP_TPSELECTOR_LISTENER;
1978 sel->u.listener = tpdata->data.factory;
1979 }
1980}
1981
1982
Benny Prijono6ba8c542007-10-16 01:34:14 +00001983/* Callback upon NAT detection completion */
1984static void nat_detect_cb(void *user_data,
1985 const pj_stun_nat_detect_result *res)
1986{
1987 PJ_UNUSED_ARG(user_data);
1988
1989 pjsua_var.nat_in_progress = PJ_FALSE;
1990 pjsua_var.nat_status = res->status;
1991 pjsua_var.nat_type = res->nat_type;
1992
1993 if (pjsua_var.ua_cfg.cb.on_nat_detect) {
1994 (*pjsua_var.ua_cfg.cb.on_nat_detect)(res);
1995 }
1996}
1997
1998
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001999/*
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002000 * Detect NAT type.
2001 */
Benny Prijono6ba8c542007-10-16 01:34:14 +00002002PJ_DEF(pj_status_t) pjsua_detect_nat_type()
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002003{
2004 pj_status_t status;
2005
Benny Prijono6ba8c542007-10-16 01:34:14 +00002006 if (pjsua_var.nat_in_progress)
2007 return PJ_SUCCESS;
2008
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002009 /* Make sure STUN server resolution has completed */
2010 status = pjsua_resolve_stun_server(PJ_TRUE);
2011 if (status != PJ_SUCCESS) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00002012 pjsua_var.nat_status = status;
2013 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002014 return status;
2015 }
2016
2017 /* Make sure we have STUN */
2018 if (pjsua_var.stun_srv.ipv4.sin_family == 0) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00002019 pjsua_var.nat_status = PJNATH_ESTUNINSERVER;
2020 return PJNATH_ESTUNINSERVER;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002021 }
2022
Benny Prijono6ba8c542007-10-16 01:34:14 +00002023 status = pj_stun_detect_nat_type(&pjsua_var.stun_srv.ipv4,
2024 &pjsua_var.stun_cfg,
2025 NULL, &nat_detect_cb);
2026
2027 if (status != PJ_SUCCESS) {
2028 pjsua_var.nat_status = status;
2029 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
2030 return status;
2031 }
2032
2033 pjsua_var.nat_in_progress = PJ_TRUE;
2034
2035 return PJ_SUCCESS;
2036}
2037
2038
2039/*
2040 * Get NAT type.
2041 */
2042PJ_DEF(pj_status_t) pjsua_get_nat_type(pj_stun_nat_type *type)
2043{
2044 *type = pjsua_var.nat_type;
2045 return pjsua_var.nat_status;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002046}
2047
2048
2049/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002050 * Verify that valid SIP url is given.
2051 */
2052PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url)
2053{
2054 pjsip_uri *p;
2055 pj_pool_t *pool;
2056 char *url;
2057 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
2058
2059 if (!len) return -1;
2060
2061 pool = pj_pool_create(&pjsua_var.cp.factory, "check%p", 1024, 0, NULL);
2062 if (!pool) return -1;
2063
Benny Prijonoa1e69682007-05-11 15:14:34 +00002064 url = (char*) pj_pool_alloc(pool, len+1);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002065 pj_ansi_strcpy(url, c_url);
2066
2067 p = pjsip_parse_uri(pool, url, len, 0);
Benny Prijonocf0b4b22007-10-06 17:31:09 +00002068 if (!p || (pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0 &&
2069 pj_stricmp2(pjsip_uri_get_scheme(p), "sips") != 0))
2070 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002071 p = NULL;
Benny Prijonocf0b4b22007-10-06 17:31:09 +00002072 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002073
2074 pj_pool_release(pool);
2075 return p ? 0 : -1;
2076}
Benny Prijonoda9785b2007-04-02 20:43:06 +00002077
2078
2079/*
2080 * This is a utility function to dump the stack states to log, using
2081 * verbosity level 3.
2082 */
2083PJ_DEF(void) pjsua_dump(pj_bool_t detail)
2084{
2085 unsigned old_decor;
2086 unsigned i;
Benny Prijonoda9785b2007-04-02 20:43:06 +00002087
2088 PJ_LOG(3,(THIS_FILE, "Start dumping application states:"));
2089
2090 old_decor = pj_log_get_decor();
2091 pj_log_set_decor(old_decor & (PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
2092
2093 if (detail)
2094 pj_dump_config();
2095
2096 pjsip_endpt_dump(pjsua_get_pjsip_endpt(), detail);
2097
2098 pjmedia_endpt_dump(pjsua_get_pjmedia_endpt());
2099
2100 PJ_LOG(3,(THIS_FILE, "Dumping media transports:"));
2101 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2102 pjsua_call *call = &pjsua_var.calls[i];
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002103 pjmedia_transport_info tpinfo;
Benny Prijono5186eae2007-12-03 14:38:25 +00002104 char addr_buf[80];
Benny Prijonoda9785b2007-04-02 20:43:06 +00002105
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002106 /* MSVC complains about tpinfo not being initialized */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002107 //pj_bzero(&tpinfo, sizeof(tpinfo));
Benny Prijono4f093d22007-04-09 08:54:32 +00002108
Benny Prijono734fc2d2008-03-17 16:05:35 +00002109 pjmedia_transport_info_init(&tpinfo);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002110 pjmedia_transport_get_info(call->med_tp, &tpinfo);
Benny Prijonoda9785b2007-04-02 20:43:06 +00002111
Benny Prijono5186eae2007-12-03 14:38:25 +00002112 PJ_LOG(3,(THIS_FILE, " %s: %s",
Benny Prijonoda9785b2007-04-02 20:43:06 +00002113 (pjsua_var.media_cfg.enable_ice ? "ICE" : "UDP"),
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002114 pj_sockaddr_print(&tpinfo.sock_info.rtp_addr_name, addr_buf,
Benny Prijono5186eae2007-12-03 14:38:25 +00002115 sizeof(addr_buf), 3)));
Benny Prijonoda9785b2007-04-02 20:43:06 +00002116 }
2117
2118 pjsip_tsx_layer_dump(detail);
2119 pjsip_ua_dump(detail);
2120
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002121// Dumping complete call states may require a 'large' buffer
2122// (about 3KB per call session, including RTCP XR).
2123#if 0
Benny Prijonoda9785b2007-04-02 20:43:06 +00002124 /* Dump all invite sessions: */
2125 PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
2126
2127 if (pjsua_call_get_count() == 0) {
2128
2129 PJ_LOG(3,(THIS_FILE, " - no sessions -"));
2130
2131 } else {
2132 unsigned i;
2133
2134 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2135 if (pjsua_call_is_active(i)) {
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002136 /* Tricky logging, since call states log string tends to be
2137 * longer than PJ_LOG_MAX_SIZE.
2138 */
2139 char buf[1024 * 3];
2140 unsigned call_dump_len;
2141 unsigned part_len;
2142 unsigned part_idx;
2143 unsigned log_decor;
2144
Benny Prijonoda9785b2007-04-02 20:43:06 +00002145 pjsua_call_dump(i, detail, buf, sizeof(buf), " ");
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002146 call_dump_len = strlen(buf);
2147
2148 log_decor = pj_log_get_decor();
2149 pj_log_set_decor(log_decor & ~(PJ_LOG_HAS_NEWLINE |
2150 PJ_LOG_HAS_CR));
2151 PJ_LOG(3,(THIS_FILE, "\n"));
2152 pj_log_set_decor(0);
2153
2154 part_idx = 0;
2155 part_len = PJ_LOG_MAX_SIZE-80;
2156 while (part_idx < call_dump_len) {
2157 char p_orig, *p;
2158
2159 p = &buf[part_idx];
2160 if (part_idx + part_len > call_dump_len)
2161 part_len = call_dump_len - part_idx;
2162 p_orig = p[part_len];
2163 p[part_len] = '\0';
2164 PJ_LOG(3,(THIS_FILE, "%s", p));
2165 p[part_len] = p_orig;
2166 part_idx += part_len;
2167 }
2168 pj_log_set_decor(log_decor);
Benny Prijonoda9785b2007-04-02 20:43:06 +00002169 }
2170 }
2171 }
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002172#endif
Benny Prijonoda9785b2007-04-02 20:43:06 +00002173
2174 /* Dump presence status */
2175 pjsua_pres_dump(detail);
2176
2177 pj_log_set_decor(old_decor);
2178 PJ_LOG(3,(THIS_FILE, "Dump complete"));
2179}
2180