aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2010-08-23 13:06:57 +0000
committerrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2010-08-23 13:06:57 +0000
commitd92034ae2fa3b27f06d5e213645e55b4a7d9e541 (patch)
tree0942d2fdb84779e41052a114da4a130f7a5d322c
parent815b5b09da5e555add7bba3d8fca588e7611248a (diff)
parentdc52cb4acbf5069b4f3aa81722361041f572b9ca (diff)
Don't blow up on an invalid AMA flag.
git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.8@283209 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--CHANGES14
-rw-r--r--UPGRADE.txt8
-rw-r--r--apps/app_celgenuserevent.c3
-rw-r--r--apps/app_dial.c3
-rw-r--r--apps/app_osplookup.c8
-rw-r--r--apps/app_readexten.c2
-rw-r--r--channels/chan_dahdi.c3
-rw-r--r--channels/chan_iax2.c110
-rw-r--r--channels/chan_local.c32
-rw-r--r--channels/chan_mgcp.c2
-rw-r--r--channels/chan_multicast_rtp.c10
-rw-r--r--channels/chan_sip.c298
-rw-r--r--channels/sig_analog.c10
-rw-r--r--channels/sig_analog.h2
-rw-r--r--channels/sig_pri.c17
-rw-r--r--channels/sig_pri.h4
-rw-r--r--channels/sip/config_parser.c12
-rw-r--r--channels/sip/include/sip.h2
-rw-r--r--configs/cdr.conf.sample31
-rw-r--r--configs/cel_adaptive_odbc.conf.sample2
-rw-r--r--configs/cel_custom.conf.sample12
-rw-r--r--configs/iax.conf.sample9
-rw-r--r--configs/res_stun_monitor.conf.sample22
-rw-r--r--configs/say.conf.sample104
-rw-r--r--configs/sip.conf.sample50
-rwxr-xr-xconfigure57
-rw-r--r--configure.ac32
-rw-r--r--doc/tex/asterisk.tex3
-rw-r--r--doc/tex/sounds.tex80
-rw-r--r--include/asterisk/ccss.h19
-rw-r--r--include/asterisk/event_defs.h4
-rw-r--r--include/asterisk/netsock2.h20
-rw-r--r--include/asterisk/pbx.h8
-rw-r--r--include/asterisk/sched.h10
-rw-r--r--include/asterisk/translate.h8
-rw-r--r--include/asterisk/unaligned.h2
-rw-r--r--main/ccss.c49
-rw-r--r--main/cdr.c39
-rw-r--r--main/cel.c5
-rw-r--r--main/channel.c14
-rw-r--r--main/cli.c43
-rw-r--r--main/netsock2.c16
-rw-r--r--main/pbx.c43
-rw-r--r--main/sched.c2
-rw-r--r--main/translate.c295
-rw-r--r--main/utils.c16
-rw-r--r--pbx/pbx_config.c6
-rw-r--r--res/res_fax.c128
-rw-r--r--res/res_stun_monitor.c311
49 files changed, 1635 insertions, 345 deletions
diff --git a/CHANGES b/CHANGES
index c4fd00ec2..681d7f0f5 100644
--- a/CHANGES
+++ b/CHANGES
@@ -73,6 +73,8 @@ SIP Changes
RTP has been outfitted with the same abilities.
* Added support for setting the Max-Forwards: header in SIP requests. Setting is
available in device configurations as well as in the dial plan.
+ * Addition of the 'subscribe_network_change' option for turning on and off
+ res_stun_monitor module support in chan_sip.
IAX2 Changes
-----------
@@ -82,6 +84,9 @@ IAX2 Changes
encryption is being used. This interoperates with the SIP SRTP implementation
so that a secure SIP call can be bridged to a secure IAX call when the
dialplan requires bridged channels to be "secure".
+ * Addition of the 'subscribe_network_change' option for turning on and off
+ res_stun_monitor module support in chan_iax.
+
MGCP Changes
------------
@@ -535,6 +540,10 @@ Miscellaneous
* The UNISTIM channel driver (chan_unistim) has been updated to support devices that
have less than 3 lines on the LCD.
* Realtime now supports database failover. See the sample extconfig.conf for details.
+ * The addition of improved translation path building for wideband codecs. Sample
+ rate changes during translation are now avoided unless absolutely necessary.
+ * The addition of the res_stun_monitor module for monitoring and reacting to network
+ changes while behind a NAT.
CLI Changes
-----------
@@ -550,6 +559,7 @@ CLI Changes
manager.conf.
* Added 'all' keyword to the CLI command "channel request hangup" so that you can send
the channel hangup request to all channels.
+ * Added a "core reload" CLI command that executes a global reload of Asterisk.
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 1.6.1 to Asterisk 1.6.2 -------------
@@ -1091,10 +1101,6 @@ SIP changes
option is enabled, Asterisk will watch for a CNG tone in the incoming audio
for a received call. If it is detected, the channel will jump to the
'fax' extension in the dialplan.
- * Improved NAT and STUN support.
- chan_sip now can use port numbers in bindaddr, externip and externhost
- options, as well as contact a STUN server to detect its external address
- for the SIP socket. See sip.conf.sample, 'NAT' section.
* The default SIP useragent= identifier now includes the Asterisk version
* A new option, match_auth_username in sip.conf changes the matching of incoming requests.
If set, and the incoming request carries authentication info,
diff --git a/UPGRADE.txt b/UPGRADE.txt
index 677fdb7c2..9202cfb10 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -20,6 +20,9 @@
From 1.6.2 to 1.8:
+* The default value for the alwaysauthreject option in sip.conf has been changed
+ from "no" to "yes".
+
* The behavior of the 'parkedcallstimeout' has changed slightly. The formulation
of the extension name that a timed out parked call is delivered to when this
option is set to 'no' was modified such that instead of converting '/' to '0',
@@ -106,6 +109,11 @@ From 1.6.2 to 1.8:
482 Loop Detected response. The dialplan will just continue from where it
left off.
+* The 'stunaddr' option has been removed from chan_sip. This feature did not
+ behave as expected, had no correct use case, and was not RFC compliant. The
+ removal of this feature will hopefully be followed by a correct RFC compliant
+ STUN implementation in chan_sip in the future.
+
From 1.6.1 to 1.6.2:
* SIP no longer sends the 183 progress message for early media by
diff --git a/apps/app_celgenuserevent.c b/apps/app_celgenuserevent.c
index da1c246ef..b1701ba23 100644
--- a/apps/app_celgenuserevent.c
+++ b/apps/app_celgenuserevent.c
@@ -41,6 +41,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<parameter name="event-name" required="true">
<argument name="event-name" required="true">
</argument>
+ <argument name="extra" required="false">
+ <para>Extra text to be included with the event.</para>
+ </argument>
</parameter>
</syntax>
<description>
diff --git a/apps/app_dial.c b/apps/app_dial.c
index 60bf7b424..0f7b4880d 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -2611,8 +2611,9 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
sentringing = 0;
ast_indicate(chan, -1);
}
- /* Be sure no generators are left on it */
+ /* Be sure no generators are left on it and reset the visible indication */
ast_deactivate_generator(chan);
+ chan->visible_indication = 0;
/* Make sure channels are compatible */
res = ast_channel_make_compatible(chan, peer);
if (res < 0) {
diff --git a/apps/app_osplookup.c b/apps/app_osplookup.c
index ea20cee77..239d6d501 100644
--- a/apps/app_osplookup.c
+++ b/apps/app_osplookup.c
@@ -1493,6 +1493,7 @@ static int osp_lookup(
char callingnum[OSP_SIZE_NORSTR];
char callednum[OSP_SIZE_NORSTR];
char destination[OSP_SIZE_NORSTR];
+ char* tmp;
unsigned int tokenlen;
char token[OSP_SIZE_TOKSTR];
char src[OSP_SIZE_NORSTR];
@@ -1565,6 +1566,11 @@ static int osp_lookup(
}
}
+ ast_copy_string(callednum, called, sizeof(callednum));
+ if((tmp = strchr(callednum, ';')) != NULL) {
+ *tmp = '\0';
+ }
+
callidnum = 0;
callids[0] = NULL;
for (i = 0; i < OSP_CALLID_MAXNUM; i++) {
@@ -1605,7 +1611,7 @@ static int osp_lookup(
dev,
calling ? calling : "",
OSPC_NFORMAT_E164,
- called,
+ callednum,
OSPC_NFORMAT_E164,
NULL,
callidnum,
diff --git a/apps/app_readexten.c b/apps/app_readexten.c
index 8049fc949..5e8159b35 100644
--- a/apps/app_readexten.c
+++ b/apps/app_readexten.c
@@ -232,7 +232,7 @@ static int readexten_exec(struct ast_channel *chan, const char *data)
if (res < 1) { /* timeout expired or hangup */
if (ast_check_hangup(chan)) {
status = "HANGUP";
- } else {
+ } else if (x == 0) {
pbx_builtin_setvar_helper(chan, arglist.variable, "t");
status = "TIMEOUT";
}
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index df03a5805..58819d08f 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -2887,6 +2887,7 @@ static void my_pri_fixup_chans(void *chan_old, void *chan_new)
/* More stuff to transfer to the new channel. */
new_chan->law = old_chan->law;
+ strcpy(new_chan->dialstring, old_chan->dialstring);
}
#endif /* defined(HAVE_PRI) */
@@ -3111,7 +3112,7 @@ static void dahdi_pri_update_span_devstate(struct sig_pri_span *pri)
if (pri->pvts[idx]->owner
#if defined(HAVE_PRI_SERVICE_MESSAGES)
/* Out-of-service B channels are "in-use". */
- && pri->pvts[idx]->service_status
+ || pri->pvts[idx]->service_status
#endif /* defined(HAVE_PRI_SERVICE_MESSAGES) */
) {
++in_use;
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 5bb6b2c59..4a3dd9ab5 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -268,6 +268,9 @@ static char default_parkinglot[AST_MAX_CONTEXT];
static char language[MAX_LANGUAGE] = "";
static char regcontext[AST_MAX_CONTEXT] = "";
+static struct ast_event_sub *network_change_event_subscription; /*!< subscription id for network change events */
+static int network_change_event_sched_id = -1;
+
static int maxauthreq = 3;
static int max_retries = 4;
static int ping_time = 21;
@@ -355,6 +358,8 @@ static int (*iax2_regfunk)(const char *username, int onoff) = NULL;
static struct io_context *io;
static struct ast_sched_thread *sched;
+#define DONT_RESCHEDULE -2
+
static format_t iax2_capability = IAX_CAPABILITY_FULLBANDWIDTH;
static int iaxdebug = 0;
@@ -1177,6 +1182,8 @@ static int iax2_setoption(struct ast_channel *c, int option, void *data, int dat
static int iax2_queryoption(struct ast_channel *c, int option, void *data, int *datalen);
static int iax2_transfer(struct ast_channel *c, const char *dest);
static int iax2_write(struct ast_channel *c, struct ast_frame *f);
+static int iax2_sched_add(struct ast_sched_thread *st, int when, ast_sched_cb callback, const void *data);
+
static int send_trunk(struct iax2_trunk_peer *tpeer, struct timeval *now);
static int send_command(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
static int send_command_final(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
@@ -1201,6 +1208,7 @@ static void build_rand_pad(unsigned char *buf, ssize_t len);
static struct callno_entry *get_unused_callno(int trunk, int validated);
static int replace_callno(const void *obj);
static void sched_delay_remove(struct sockaddr_in *sin, struct callno_entry *callno_entry);
+static void network_change_event_cb(const struct ast_event *, void *);
static const struct ast_channel_tech iax2_tech = {
.type = "IAX2",
@@ -1236,6 +1244,47 @@ static void mwi_event_cb(const struct ast_event *event, void *userdata)
* is time to send MWI, since it is only sent with a REGACK. */
}
+static void network_change_event_subscribe(void)
+{
+ if (!network_change_event_subscription) {
+ network_change_event_subscription = ast_event_subscribe(AST_EVENT_NETWORK_CHANGE,
+ network_change_event_cb,
+ "SIP Network Change ",
+ NULL,
+ AST_EVENT_IE_END);
+ }
+}
+
+static void network_change_event_unsubscribe(void)
+{
+ if (network_change_event_subscription) {
+ network_change_event_subscription = ast_event_unsubscribe(network_change_event_subscription);
+ }
+}
+
+static int network_change_event_sched_cb(const void *data)
+{
+ struct iax2_registry *reg;
+ network_change_event_sched_id = -1;
+ AST_LIST_LOCK(&registrations);
+ AST_LIST_TRAVERSE(&registrations, reg, entry) {
+ iax2_do_register(reg);
+ }
+ AST_LIST_UNLOCK(&registrations);
+
+ return 0;
+}
+
+static void network_change_event_cb(const struct ast_event *event, void *userdata)
+{
+ ast_debug(1, "IAX, got a network change event, renewing all IAX registrations.\n");
+ if (network_change_event_sched_id == -1) {
+ network_change_event_sched_id = iax2_sched_add(sched, 1000, network_change_event_sched_cb, NULL);
+ }
+
+}
+
+
/*! \brief Send manager event at call setup to link between Asterisk channel name
and IAX2 call identifiers */
static void iax2_ami_channelupdate(struct chan_iax2_pvt *pvt)
@@ -1246,7 +1295,6 @@ static void iax2_ami_channelupdate(struct chan_iax2_pvt *pvt)
pvt->callno, pvt->peercallno, pvt->peer ? pvt->peer : "");
}
-
static struct ast_datastore_info iax2_variable_datastore_info = {
.type = "IAX2_VARIABLE",
.duplicate = iax2_dup_variable_datastore,
@@ -1427,10 +1475,9 @@ static void __send_ping(const void *data)
if (iaxs[callno]) {
if (iaxs[callno]->peercallno) {
send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1);
- iaxs[callno]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, data);
- } else {
- /* I am the schedule, so I'm allowed to do this */
- iaxs[callno]->pingid = -1;
+ if (iaxs[callno]->pingid != DONT_RESCHEDULE) {
+ iaxs[callno]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, data);
+ }
}
} else {
ast_debug(1, "I was supposed to send a PING with callno %d, but no such call exists.\n", callno);
@@ -1441,9 +1488,16 @@ static void __send_ping(const void *data)
static int send_ping(const void *data)
{
+ int callno = (long) data;
+ ast_mutex_lock(&iaxsl[callno]);
+ if (iaxs[callno] && iaxs[callno]->pingid != DONT_RESCHEDULE) {
+ iaxs[callno]->pingid = -1;
+ }
+ ast_mutex_unlock(&iaxsl[callno]);
+
#ifdef SCHED_MULTITHREADED
if (schedule_action(__send_ping, data))
-#endif
+#endif
__send_ping(data);
return 0;
@@ -1488,10 +1542,9 @@ static void __send_lagrq(const void *data)
if (iaxs[callno]) {
if (iaxs[callno]->peercallno) {
send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
- iaxs[callno]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, data);
- } else {
- /* I am the schedule, so I'm allowed to do this */
- iaxs[callno]->lagid = -1;
+ if (iaxs[callno]->lagid != DONT_RESCHEDULE) {
+ iaxs[callno]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, data);
+ }
}
} else {
ast_debug(1, "I was supposed to send a LAGRQ with callno %d, but no such call exists.\n", callno);
@@ -1502,11 +1555,17 @@ static void __send_lagrq(const void *data)
static int send_lagrq(const void *data)
{
+ int callno = (long) data;
+ ast_mutex_lock(&iaxsl[callno]);
+ if (iaxs[callno] && iaxs[callno]->lagid != DONT_RESCHEDULE) {
+ iaxs[callno]->lagid = -1;
+ }
+ ast_mutex_unlock(&iaxsl[callno]);
+
#ifdef SCHED_MULTITHREADED
if (schedule_action(__send_lagrq, data))
-#endif
+#endif
__send_lagrq(data);
-
return 0;
}
@@ -1688,7 +1747,9 @@ static void iax2_destroy_helper(struct chan_iax2_pvt *pvt)
}
/* No more pings or lagrq's */
AST_SCHED_DEL_SPINLOCK(ast_sched_thread_get_context(sched), pvt->pingid, &iaxsl[pvt->callno]);
+ pvt->pingid = DONT_RESCHEDULE;
AST_SCHED_DEL_SPINLOCK(ast_sched_thread_get_context(sched), pvt->lagid, &iaxsl[pvt->callno]);
+ pvt->lagid = DONT_RESCHEDULE;
ast_sched_thread_del(sched, pvt->autoid);
ast_sched_thread_del(sched, pvt->authid);
ast_sched_thread_del(sched, pvt->initid);
@@ -10428,6 +10489,7 @@ retryowner:
/* No authentication required, let them in */
memset(&ied1, 0, sizeof(ied1));
iax_ie_append_int(&ied1, IAX_IE_FORMAT, format);
+ iax_ie_append_versioned_uint64(&ied1, IAX_IE_FORMAT2, 0, format);
send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1);
if (strcmp(iaxs[fr->callno]->exten, "TBD")) {
ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
@@ -10893,6 +10955,7 @@ retryowner2:
/* Authentication received */
memset(&ied1, 0, sizeof(ied1));
iax_ie_append_int(&ied1, IAX_IE_FORMAT, format);
+ iax_ie_append_versioned_uint64(&ied1, IAX_IE_FORMAT2, 0, format);
send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1);
if (strcmp(iaxs[fr->callno]->exten, "TBD")) {
ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
@@ -12779,7 +12842,8 @@ static int set_config(const char *config_file, int reload)
int format;
int portno = IAX_DEFAULT_PORTNO;
int x;
- int mtuv;
+ int mtuv;
+ int subscribe_network_change = 1;
struct iax2_user *user;
struct iax2_peer *peer;
struct ast_netsock *ns;
@@ -13097,6 +13161,14 @@ static int set_config(const char *config_file, int reload)
if (add_calltoken_ignore(v->value)) {
ast_log(LOG_WARNING, "Invalid calltokenoptional address range - '%s' line %d\n", v->value, v->lineno);
}
+ } else if (!strcasecmp(v->name, "subscribe_network_change_event")) {
+ if (ast_true(v->value)) {
+ subscribe_network_change = 1;
+ } else if (ast_false(v->value)) {
+ subscribe_network_change = 0;
+ } else {
+ ast_log(LOG_WARNING, "subscribe_network_change_event value %s is not valid at line %d.\n", v->value, v->lineno);
+ }
} else if (!strcasecmp(v->name, "shrinkcallerid")) {
if (ast_true(v->value)) {
ast_set_flag64((&globalflags), IAX_SHRINKCALLERID);
@@ -13109,7 +13181,13 @@ static int set_config(const char *config_file, int reload)
/* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
v = v->next;
}
-
+
+ if (subscribe_network_change) {
+ network_change_event_subscribe();
+ } else {
+ network_change_event_unsubscribe();
+ }
+
if (defaultsockfd < 0) {
if (!(ns = ast_netsock_bind(netsock, io, "0.0.0.0", portno, qos.tos, qos.cos, socket_read, NULL))) {
ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
@@ -14050,6 +14128,8 @@ static int __unload_module(void)
struct ast_context *con;
int x;
+ network_change_event_unsubscribe();
+
ast_manager_unregister("IAXpeers");
ast_manager_unregister("IAXpeerlist");
ast_manager_unregister("IAXnetstats");
@@ -14530,6 +14610,8 @@ static int load_module(void)
ast_realtime_require_field("iaxpeers", "name", RQ_CHAR, 10, "ipaddr", RQ_CHAR, 15, "port", RQ_UINTEGER2, 5, "regseconds", RQ_UINTEGER2, 6, SENTINEL);
+ network_change_event_subscribe();
+
return AST_MODULE_LOAD_SUCCESS;
}
diff --git a/channels/chan_local.c b/channels/chan_local.c
index d1f66f8dd..aebca441a 100644
--- a/channels/chan_local.c
+++ b/channels/chan_local.c
@@ -409,6 +409,38 @@ static void check_bridge(struct local_pvt *p)
p->chan->audiohooks = p->owner->audiohooks;
p->owner->audiohooks = audiohooks_swapper;
}
+
+ /* If any Caller ID was set, preserve it after masquerade like above. We must check
+ * to see if Caller ID was set because otherwise we'll mistakingly copy info not
+ * set from the dialplan and will overwrite the real channel Caller ID. The reason
+ * for this whole preswapping action is because the Caller ID is set on the channel
+ * thread (which is the to be masqueraded away local channel) before both local
+ * channels are optimized away.
+ */
+ if (p->owner->caller.id.name.valid || p->owner->caller.id.number.valid
+ || p->owner->caller.id.subaddress.valid || p->owner->caller.ani.name.valid
+ || p->owner->caller.ani.number.valid || p->owner->caller.ani.subaddress.valid) {
+ struct ast_party_caller tmp;
+ tmp = p->owner->caller;
+ p->owner->caller = p->chan->_bridge->caller;
+ p->chan->_bridge->caller = tmp;
+ }
+ if (p->owner->redirecting.from.name.valid || p->owner->redirecting.from.number.valid
+ || p->owner->redirecting.from.subaddress.valid || p->owner->redirecting.to.name.valid
+ || p->owner->redirecting.to.number.valid || p->owner->redirecting.to.subaddress.valid) {
+ struct ast_party_redirecting tmp;
+ tmp = p->owner->redirecting;
+ p->owner->redirecting = p->chan->_bridge->redirecting;
+ p->chan->_bridge->redirecting = tmp;
+ }
+ if (p->owner->dialed.number.str || p->owner->dialed.subaddress.valid) {
+ struct ast_party_dialed tmp;
+ tmp = p->owner->dialed;
+ p->owner->dialed = p->chan->_bridge->dialed;
+ p->chan->_bridge->dialed = tmp;
+ }
+
+
ast_app_group_update(p->chan, p->owner);
ast_channel_masquerade(p->owner, p->chan->_bridge);
ast_set_flag(p, LOCAL_ALREADY_MASQED);
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index 485b39aad..a787eab72 100644
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -3580,7 +3580,7 @@ static int find_and_retrans(struct mgcp_subchannel *sub, struct mgcp_request *re
if (sscanf(req->identifier, "%30d", &seqno) != 1) {
seqno = 0;
}
- for (cur = sub->parent->parent->responses, next = cur->next; cur; cur = next, next = cur->next) {
+ for (cur = sub->parent->parent->responses, next = cur ? cur->next : NULL; cur; cur = next, next = cur ? cur->next : NULL) {
if (now - cur->whensent > RESPONSE_TIMEOUT) {
/* Delete this entry */
if (prev)
diff --git a/channels/chan_multicast_rtp.c b/channels/chan_multicast_rtp.c
index e3414d2f3..93f46473f 100644
--- a/channels/chan_multicast_rtp.c
+++ b/channels/chan_multicast_rtp.c
@@ -126,11 +126,6 @@ static struct ast_channel *multicast_rtp_request(const char *type, format_t form
}
*destination++ = '\0';
- if (!ast_sockaddr_parse(&destination_address, destination,
- PARSE_PORT_REQUIRE)) {
- goto failure;
- }
-
if ((control = strchr(destination, '/'))) {
*control++ = '\0';
if (!ast_sockaddr_parse(&control_address, control,
@@ -139,6 +134,11 @@ static struct ast_channel *multicast_rtp_request(const char *type, format_t form
}
}
+ if (!ast_sockaddr_parse(&destination_address, destination,
+ PARSE_PORT_REQUIRE)) {
+ goto failure;
+ }
+
if (!(instance = ast_rtp_instance_new("multicast", NULL, &control_address, multicast_type))) {
goto failure;
}
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 47dc2610d..40e679209 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -259,7 +259,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/translate.h"
#include "asterisk/ast_version.h"
#include "asterisk/event.h"
-#include "asterisk/stun.h"
#include "asterisk/cel.h"
#include "asterisk/data.h"
#include "asterisk/aoc.h"
@@ -762,6 +761,9 @@ static int regobjs = 0; /*!< Registry objects */
static struct ast_flags global_flags[3] = {{0}}; /*!< global SIP_ flags */
static int global_t38_maxdatagram; /*!< global T.38 FaxMaxDatagram override */
+static struct ast_event_sub *network_change_event_subscription; /*!< subscription id for network change events */
+static int network_change_event_sched_id = -1;
+
static char used_context[AST_MAX_CONTEXT]; /*!< name of automatically created context for unloading */
AST_MUTEX_DEFINE_STATIC(netlock);
@@ -1142,9 +1144,6 @@ static struct ast_sockaddr internip;
* hostname is stored in externhost, and the hostname->IP mapping
* is refreshed every 'externrefresh' seconds;
*
- * + with "stunaddr = host[:port]" we run queries every externrefresh seconds
- * to the specified server, and store the result in externaddr.
- *
* Other variables (externhost, externexpire, externrefresh) are used
* to support the above functions.
*/
@@ -1154,7 +1153,6 @@ static struct ast_sockaddr media_address; /*!< External RTP IP address if we are
static char externhost[MAXHOSTNAMELEN]; /*!< External host name */
static time_t externexpire; /*!< Expiration counter for re-resolving external host name in dynamic DNS */
static int externrefresh = 10; /*!< Refresh timer for DNS-based external address (dyndns) */
-static struct sockaddr_in stunaddr; /*!< stun server address */
static uint16_t externtcpport; /*!< external tcp port */
static uint16_t externtlsport; /*!< external tls port */
@@ -1334,6 +1332,7 @@ static int sip_poke_peer(struct sip_peer *peer, int force);
static void sip_poke_all_peers(void);
static void sip_peer_hold(struct sip_pvt *p, int hold);
static void mwi_event_cb(const struct ast_event *, void *);
+static void network_change_event_cb(const struct ast_event *, void *);
/*--- Applications, functions, CLI and manager command helpers */
static const char *sip_nat_mode(const struct sip_pvt *p);
@@ -2622,6 +2621,32 @@ cleanup:
return NULL;
}
+/* this func is used with ao2_callback to unlink/delete all marked
+ peers */
+static int peer_is_marked(void *peerobj, void *arg, int flags)
+{
+ struct sip_peer *peer = peerobj;
+ return peer->the_mark ? CMP_MATCH : 0;
+}
+
+
+/* \brief Unlink all marked peers from ao2 containers */
+static void unlink_marked_peers_from_tables(void)
+{
+ ao2_t_callback(peers, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, peer_is_marked, NULL,
+ "initiating callback to remove marked peers");
+ ao2_t_callback(peers_by_ip, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, peer_is_marked, NULL,
+ "initiating callback to remove marked peers");
+}
+
+/* \brief Unlink single peer from all ao2 containers */
+static void unlink_peer_from_tables(struct sip_peer *peer)
+{
+ ao2_t_unlink(peers, peer, "ao2_unlink of peer from peers table");
+ if (!ast_sockaddr_isnull(&peer->addr)) {
+ ao2_t_unlink(peers_by_ip, peer, "ao2_unlink of peer from peers_by_ip table");
+ }
+}
/*!
* helper functions to unreference various types of objects.
@@ -3112,14 +3137,13 @@ static void build_via(struct sip_pvt *p)
static void ast_sip_ouraddrfor(const struct ast_sockaddr *them, struct ast_sockaddr *us, struct sip_pvt *p)
{
struct ast_sockaddr theirs;
- struct sockaddr_in externaddr_sin;
/* Set want_remap to non-zero if we want to remap 'us' to an externally
* reachable IP address and port. This is done if:
* 1. we have a localaddr list (containing 'internal' addresses marked
* as 'deny', so ast_apply_ha() will return AST_SENSE_DENY on them,
* and AST_SENSE_ALLOW on 'external' ones);
- * 2. either stunaddr or externaddr is set, so we know what to use as the
+ * 2. externaddr is set, so we know what to use as the
* externally visible address;
* 3. the remote address, 'them', is external;
* 4. the address returned by ast_ouraddrfor() is 'internal' (AST_SENSE_DENY
@@ -3141,22 +3165,16 @@ static void ast_sip_ouraddrfor(const struct ast_sockaddr *them, struct ast_socka
}
} else {
want_remap = localaddr &&
- !(ast_sockaddr_isnull(&externaddr) && stunaddr.sin_addr.s_addr) &&
+ !ast_sockaddr_isnull(&externaddr) &&
ast_apply_ha(localaddr, &theirs) == AST_SENSE_ALLOW ;
}
if (want_remap &&
(!sip_cfg.matchexternaddrlocally || !ast_apply_ha(localaddr, us)) ) {
- /* if we used externhost or stun, see if it is time to refresh the info */
+ /* if we used externhost, see if it is time to refresh the info */
if (externexpire && time(NULL) >= externexpire) {
- if (stunaddr.sin_addr.s_addr) {
- ast_sockaddr_to_sin(&externaddr, &externaddr_sin);
- ast_stun_request(sipsock, &stunaddr, NULL, &externaddr_sin);
- } else {
- if (ast_sockaddr_resolve_first(&externaddr, externhost, 0)) {
- ast_log(LOG_NOTICE, "Warning: Re-lookup of '%s' failed!\n", externhost);
- }
- externexpire = time(NULL);
+ if (ast_sockaddr_resolve_first(&externaddr, externhost, 0)) {
+ ast_log(LOG_NOTICE, "Warning: Re-lookup of '%s' failed!\n", externhost);
}
externexpire = time(NULL) + externrefresh;
}
@@ -3182,9 +3200,6 @@ static void ast_sip_ouraddrfor(const struct ast_sockaddr *them, struct ast_socka
break;
}
}
- else {
- ast_log(LOG_WARNING, "stun failed\n");
- }
ast_debug(1, "Target address %s is not local, substituting externaddr\n",
ast_sockaddr_stringify(them));
} else if (p) {
@@ -8548,7 +8563,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
ast_set_write_format(p->owner, p->owner->writeformat);
}
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) && !ast_sockaddr_isnull(sa) && (!sendonly || sendonly == -1)) {
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) && (!ast_sockaddr_isnull(sa) || !ast_sockaddr_isnull(vsa) || !ast_sockaddr_isnull(tsa) || !ast_sockaddr_isnull(isa)) && (!sendonly || sendonly == -1)) {
ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
/* Activate a re-invite */
ast_queue_frame(p->owner, &ast_null_frame);
@@ -8564,7 +8579,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
if (sip_cfg.notifyhold)
sip_peer_hold(p, FALSE);
ast_clear_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD); /* Clear both flags */
- } else if (ast_sockaddr_isnull(sa) || (sendonly && sendonly != -1)) {
+ } else if ((ast_sockaddr_isnull(sa) && ast_sockaddr_isnull(vsa) && ast_sockaddr_isnull(tsa) && ast_sockaddr_isnull(isa)) || (sendonly && sendonly != -1)) {
int already_on_hold = ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD);
ast_queue_control_data(p->owner, AST_CONTROL_HOLD,
S_OR(p->mohsuggest, NULL),
@@ -9035,8 +9050,16 @@ static int add_header_max_forwards(struct sip_pvt *dialog, struct sip_request *r
char clen[10];
const char *max = NULL;
+ /* deadlock avoidance */
+ while (dialog->owner && ast_channel_trylock(dialog->owner)) {
+ sip_pvt_unlock(dialog);
+ usleep(1);
+ sip_pvt_lock(dialog);
+ }
+
if (dialog->owner) {
max = pbx_builtin_getvar_helper(dialog->owner, "SIP_MAX_FORWARDS");
+ ast_channel_unlock(dialog->owner);
}
/* The channel variable overrides the peer/channel value */
@@ -10259,6 +10282,8 @@ static void get_our_media_address(struct sip_pvt *p, int needvideo, int needtext
struct ast_sockaddr *taddr, struct ast_sockaddr *dest,
struct ast_sockaddr *vdest, struct ast_sockaddr *tdest)
{
+ int use_externip = 0;
+
/* First, get our address */
ast_rtp_instance_get_local_address(p->rtp, addr);
if (p->vrtp) {
@@ -10268,6 +10293,11 @@ static void get_our_media_address(struct sip_pvt *p, int needvideo, int needtext
ast_rtp_instance_get_local_address(p->trtp, taddr);
}
+ /* If our real IP differs from the local address returned by the RTP engine, use it. */
+ /* The premise is that if we are already using that IP to communicate with the client, */
+ /* we should be using it for RTP too. */
+ use_externip = ast_sockaddr_cmp_addr(&p->ourip, addr);
+
/* Now, try to figure out where we want them to send data */
/* Is this a re-invite to move the media out, then use the original offer from caller */
if (!ast_sockaddr_isnull(&p->redirip)) { /* If we have a redirection IP, use it */
@@ -10286,7 +10316,7 @@ static void get_our_media_address(struct sip_pvt *p, int needvideo, int needtext
*/
ast_sockaddr_copy(dest,
!ast_sockaddr_isnull(&media_address) ? &media_address :
- !ast_sockaddr_is_any(addr) ? addr :
+ !ast_sockaddr_is_any(addr) && !use_externip ? addr :
&p->ourip);
ast_sockaddr_set_port(dest, ast_sockaddr_port(addr));
}
@@ -10309,7 +10339,7 @@ static void get_our_media_address(struct sip_pvt *p, int needvideo, int needtext
*/
ast_sockaddr_copy(vdest,
!ast_sockaddr_isnull(&media_address) ? &media_address :
- !ast_sockaddr_is_any(vaddr) ? vaddr :
+ !ast_sockaddr_is_any(vaddr) && !use_externip ? vaddr :
&p->ourip);
ast_sockaddr_set_port(vdest, ast_sockaddr_port(vaddr));
}
@@ -10333,7 +10363,7 @@ static void get_our_media_address(struct sip_pvt *p, int needvideo, int needtext
*/
ast_sockaddr_copy(tdest,
!ast_sockaddr_isnull(&media_address) ? &media_address :
- !ast_sockaddr_is_any(taddr) ? taddr :
+ !ast_sockaddr_is_any(taddr) && !use_externip ? taddr :
&p->ourip);
ast_sockaddr_set_port(tdest, ast_sockaddr_port(taddr));
}
@@ -11645,23 +11675,26 @@ static void state_notify_build_xml(int state, int full, const char *exten, const
ast_channel_unlock(caller);
caller = ast_channel_unref(caller);
}
+
+ /* We create a fake call-id which the phone will send back in an INVITE
+ Replaces header which we can grab and do some magic with. */
+ ast_str_append(tmp, 0,
+ "<dialog id=\"%s\" call-id=\"pickup-%s\" direction=\"recipient\">\n"
+ "<remote>\n"
+ /* See the limitations of this above. Luckily the phone seems to still be
+ happy when these values are not correct. */
+ "<identity display=\"%s\">%s</identity>\n"
+ "<target uri=\"%s\"/>\n"
+ "</remote>\n"
+ "<local>\n"
+ "<identity>%s</identity>\n"
+ "<target uri=\"%s\"/>\n"
+ "</local>\n",
+ exten, p->callid, local_display, local_target, local_target, mto, mto);
+ } else {
+ ast_str_append(tmp, 0, "<dialog id=\"%s\" direction=\"recipient\">\n", exten);
}
- /* We create a fake call-id which the phone will send back in an INVITE
- Replaces header which we can grab and do some magic with. */
- ast_str_append(tmp, 0,
- "<dialog id=\"%s\" call-id=\"pickup-%s\" direction=\"recipient\">\n"
- "<remote>\n"
- /* See the limitations of this above. Luckily the phone seems to still be
- happy when these values are not correct. */
- "<identity display=\"%s\">%s</identity>\n"
- "<target uri=\"%s\"/>\n"
- "</remote>\n"
- "<local>\n"
- "<identity>%s</identity>\n"
- "<target uri=\"%s\"/>\n"
- "</local>\n",
- exten, p->callid, local_display, local_target, local_target, mto, mto);
} else {
ast_str_append(tmp, 0, "<dialog id=\"%s\">", exten);
}
@@ -12652,7 +12685,6 @@ static int expire_register(const void *data)
peer->expire = -1;
peer->portinuri = 0;
- memset(&peer->addr, 0, sizeof(peer->addr));
destroy_association(peer); /* remove registration data from storage */
set_socket_transport(&peer->socket, peer->default_outbound_transport);
@@ -12675,12 +12707,13 @@ static int expire_register(const void *data)
if (peer->selfdestruct ||
ast_test_flag(&peer->flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
- ao2_t_unlink(peers, peer, "ao2_unlink of peer from peers table");
- if (!ast_sockaddr_isnull(&peer->addr)) {
- ao2_t_unlink(peers_by_ip, peer, "ao2_unlink of peer from peers_by_ip table");
- }
+ unlink_peer_from_tables(peer);
}
+ /* Only clear the addr after we check for destruction. The addr must remain
+ * in order to unlink from the peers_by_ip container correctly */
+ memset(&peer->addr, 0, sizeof(peer->addr));
+
unref_peer(peer, "removing peer ref for expire_register");
return 0;
@@ -13411,6 +13444,39 @@ static void mwi_event_cb(const struct ast_event *event, void *userdata)
ao2_unlock(peer);
}
+static void network_change_event_subscribe(void)
+{
+ if (!network_change_event_subscription) {
+ network_change_event_subscription = ast_event_subscribe(AST_EVENT_NETWORK_CHANGE,
+ network_change_event_cb,
+ "SIP Network Change ",
+ NULL, AST_EVENT_IE_END);
+ }
+}
+
+static void network_change_event_unsubscribe(void)
+{
+ if (network_change_event_subscription) {
+ network_change_event_subscription = ast_event_unsubscribe(network_change_event_subscription);
+ }
+}
+
+static int network_change_event_sched_cb(const void *data)
+{
+ network_change_event_sched_id = -1;
+ sip_send_all_registers();
+ sip_send_all_mwi_subscriptions();
+ return 0;
+}
+
+static void network_change_event_cb(const struct ast_event *event, void *userdata)
+{
+ ast_debug(1, "SIP, got a network change event, renewing all SIP registrations.\n");
+ if (network_change_event_sched_id == -1) {
+ network_change_event_sched_id = ast_sched_add(sched, 1000, network_change_event_sched_cb, NULL);
+ }
+}
+
/*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
\note If you add an "hint" priority to the extension in the dial plan,
you will get notifications on device state changes */
@@ -14623,7 +14689,7 @@ static int get_also_info(struct sip_pvt *p, struct sip_request *oreq)
* address and port in the SIP headers without the need for STUN.
* The address part is also reused for the media sessions.
* Note that ast_sip_ouraddrfor() still rewrites p->ourip
- * if you specify externaddr/seternaddr/stunaddr.
+ * if you specify externaddr/seternaddr/.
*/
static attribute_unused void check_via_response(struct sip_pvt *p, struct sip_request *req)
{
@@ -15790,14 +15856,6 @@ static int dialog_needdestroy(void *dialogobj, void *arg, int flags)
return 0;
}
-/* this func is used with ao2_callback to unlink/delete all marked
- peers */
-static int peer_is_marked(void *peerobj, void *arg, int flags)
-{
- struct sip_peer *peer = peerobj;
- return peer->the_mark ? CMP_MATCH : 0;
-}
-
/*! \brief Remove temporary realtime objects from memory (CLI) */
/*! \todo XXXX Propably needs an overhaul after removal of the devices */
static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
@@ -15900,8 +15958,7 @@ static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli
}
ao2_iterator_destroy(&i);
if (pruned) {
- ao2_t_callback(peers, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, peer_is_marked, NULL,
- "initiating callback to remove marked peers");
+ unlink_marked_peers_from_tables();
ast_cli(a->fd, "%d peers pruned.\n", pruned);
} else
ast_cli(a->fd, "No peers found to prune.\n");
@@ -16814,8 +16871,6 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
msg = "Disabled, no localnet list";
else if (ast_sockaddr_isnull(&externaddr))
msg = "Disabled";
- else if (stunaddr.sin_addr.s_addr != 0)
- msg = "Enabled using STUN";
else if (!ast_strlen_zero(externhost))
msg = "Enabled using externhost";
else
@@ -16835,8 +16890,6 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
ast_strdupa(ast_sockaddr_stringify_addr(&d->netmask)));
}
}
- ast_cli(a->fd, " STUN server: %s:%d\n", ast_inet_ntoa(stunaddr.sin_addr), ntohs(stunaddr.sin_port));
-
ast_cli(a->fd, "\nGlobal Signalling Settings:\n");
ast_cli(a->fd, "---------------------------\n");
ast_cli(a->fd, " Codecs: ");
@@ -19189,6 +19242,23 @@ static void handle_response_refer(struct sip_pvt *p, int resp, const char *rest,
ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
}
break;
+ default:
+ /* We should treat unrecognized 9xx as 900. 400 is actually
+ specified as a possible response, but any 4-6xx is
+ theoretically possible. */
+
+ if (resp < 299) { /* 1xx cases don't get here */
+ ast_log(LOG_WARNING, "SIP transfer to %s had unxpected 2xx response (%d), confusion is possible. \n", p->refer->refer_to, resp);
+ } else {
+ ast_log(LOG_WARNING, "SIP transfer to %s with response (%d). \n", p->refer->refer_to, resp);
+ }
+
+ p->refer->status = REFER_FAILED;
+ pvt_set_needdestroy(p, "received failure response");
+ if (p->owner) {
+ ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
+ }
+ break;
}
}
@@ -19505,6 +19575,8 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
need to hang around for something more "definitive" */
if (resp != 100)
handle_response_peerpoke(p, resp, req);
+ } else if (sipmethod == SIP_REFER && resp >= 200) {
+ handle_response_refer(p, resp, rest, req, seqno);
} else if (sipmethod == SIP_PUBLISH) {
/* SIP PUBLISH transcends this morass of doodoo and instead
* we just always call the response handler. Good gravy!
@@ -19541,18 +19613,12 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
ast_clear_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
}
break;
- case 202: /* Transfer accepted */
- if (sipmethod == SIP_REFER)
- handle_response_refer(p, resp, rest, req, seqno);
- break;
case 401: /* Not www-authorized on SIP method */
case 407: /* Proxy auth required */
if (sipmethod == SIP_INVITE)
handle_response_invite(p, resp, rest, req, seqno);
else if (sipmethod == SIP_NOTIFY)
handle_response_notify(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_REFER)
- handle_response_refer(p, resp, rest, req, seqno);
else if (sipmethod == SIP_SUBSCRIBE)
handle_response_subscribe(p, resp, rest, req, seqno);
else if (p->registry && sipmethod == SIP_REGISTER)
@@ -19625,8 +19691,6 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
case 481: /* Call leg does not exist */
if (sipmethod == SIP_INVITE) {
handle_response_invite(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_REFER) {
- handle_response_refer(p, resp, rest, req, seqno);
} else if (sipmethod == SIP_SUBSCRIBE) {
handle_response_subscribe(p, resp, rest, req, seqno);
} else if (sipmethod == SIP_BYE) {
@@ -19668,16 +19732,9 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
}
if (sipmethod == SIP_INVITE)
handle_response_invite(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_REFER)
- handle_response_refer(p, resp, rest, req, seqno);
else
ast_log(LOG_WARNING, "Host '%s' does not implement '%s'\n", ast_sockaddr_stringify(&p->sa), msg);
break;
- case 603: /* Declined transfer */
- if (sipmethod == SIP_REFER) {
- handle_response_refer(p, resp, rest, req, seqno);
- break;
- }
/* Fallthrough */
default:
if ((resp >= 300) && (resp < 700)) {
@@ -19720,10 +19777,7 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
case 410: /* Gone */
case 400: /* Bad Request */
case 500: /* Server error */
- if (sipmethod == SIP_REFER) {
- handle_response_refer(p, resp, rest, req, seqno);
- break;
- } else if (sipmethod == SIP_SUBSCRIBE) {
+ if (sipmethod == SIP_SUBSCRIBE) {
handle_response_subscribe(p, resp, rest, req, seqno);
break;
}
@@ -19821,15 +19875,9 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
;
}
break;
- case 202: /* Transfer accepted */
- if (sipmethod == SIP_REFER)
- handle_response_refer(p, resp, rest, req, seqno);
- break;
case 401: /* www-auth */
case 407:
- if (sipmethod == SIP_REFER)
- handle_response_refer(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_INVITE)
+ if (sipmethod == SIP_INVITE)
handle_response_invite(p, resp, rest, req, seqno);
else if (sipmethod == SIP_BYE) {
if (p->authtries == MAX_AUTHTRIES || do_proxy_auth(p, req, resp, sipmethod, 0)) {
@@ -19851,15 +19899,7 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
case 501: /* Not Implemented */
if (sipmethod == SIP_INVITE)
handle_response_invite(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_REFER)
- handle_response_refer(p, resp, rest, req, seqno);
break;
- case 603: /* Declined transfer */
- if (sipmethod == SIP_REFER) {
- handle_response_refer(p, resp, rest, req, seqno);
- break;
- }
- /* Fallthrough */
default: /* Errors without handlers */
if ((resp >= 100) && (resp < 200)) {
if (sipmethod == SIP_INVITE) { /* re-invite */
@@ -20307,18 +20347,8 @@ static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, str
*sep++ = '\0'; /* Response string */
respcode = atoi(code);
switch (respcode) {
- case 100: /* Trying: */
- case 101: /* dialog establishment */
- /* Don't do anything yet */
- success = -1; /* Wait */
- break;
- case 183: /* Ringing: */
- /* Don't do anything yet */
- success = -1; /* Wait */
- break;
case 200: /* OK: The new call is up, hangup this call */
/* Hangup the call that we are replacing */
- success = -1; /* Wait */
break;
case 301: /* Moved permenantly */
case 302: /* Moved temporarily */
@@ -20326,13 +20356,24 @@ static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, str
success = FALSE;
break;
case 503: /* Service Unavailable: The new call failed */
- /* Cancel transfer, continue the call */
+ case 603: /* Declined: Not accepted */
+ /* Cancel transfer, continue the current call */
success = FALSE;
break;
- case 603: /* Declined: Not accepted */
+ case 0: /* Parse error */
/* Cancel transfer, continue the current call */
+ ast_log(LOG_NOTICE, "Error parsing sipfrag in NOTIFY in response to REFER.\n");
success = FALSE;
break;
+ default:
+ if (respcode < 200) {
+ /* ignore provisional responses */
+ success = -1;
+ } else {
+ ast_log(LOG_NOTICE, "Got unknown code '%d' in NOTIFY in response to REFER.\n", respcode);
+ success = FALSE;
+ }
+ break;
}
if (success == FALSE) {
ast_log(LOG_NOTICE, "Transfer failed. Sorry. Nothing further to do with this call\n");
@@ -25343,6 +25384,8 @@ static void set_peer_defaults(struct sip_peer *peer)
peer->timer_b = global_timer_b;
clear_peer_mailboxes(peer);
peer->disallowed_methods = sip_cfg.disallowed_methods;
+ peer->transports = default_transports;
+ peer->default_outbound_transport = default_primary_transport;
}
/*! \brief Create temporary peer (used in autocreatepeer mode) */
@@ -25497,6 +25540,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
/* If we have realm authentication information, remove them (reload) */
clear_realm_authentication(peer->auth);
peer->auth = NULL;
+ /* clear the transport information. We will detect if a default value is required after parsing the config */
peer->default_outbound_transport = 0;
peer->transports = 0;
@@ -26100,8 +26144,8 @@ static int reload_config(enum channelreloadreason reason)
int auto_sip_domains = FALSE;
struct ast_sockaddr old_bindaddr = bindaddr;
int registry_count = 0, peer_count = 0, timerb_set = 0, timert1_set = 0;
+ int subscribe_network_change = 1;
time_t run_start, run_end;
- struct sockaddr_in externaddr_sin;
int bindport = 0;
run_start = time(0);
@@ -26204,7 +26248,6 @@ static int reload_config(enum channelreloadreason reason)
/* Reset IP addresses */
ast_sockaddr_parse(&bindaddr, "0.0.0.0:0", 0);
- memset(&stunaddr, 0, sizeof(stunaddr));
memset(&internip, 0, sizeof(internip));
/* Free memory for local network address mask */
@@ -26590,12 +26633,6 @@ static int reload_config(enum channelreloadreason reason)
}
} else if (!strcasecmp(v->name, "registerattempts")) {
global_regattempts_max = atoi(v->value);
- } else if (!strcasecmp(v->name, "stunaddr")) {
- stunaddr.sin_port = htons(3478);
- if (ast_parse_arg(v->value, PARSE_INADDR, &stunaddr)) {
- ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", v->value);
- }
- externexpire = time(NULL);
} else if (!strcasecmp(v->name, "bindaddr") || !strcasecmp(v->name, "udpbindaddr")) {
if (ast_parse_arg(v->value, PARSE_ADDR, &bindaddr)) {
ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
@@ -26803,11 +26840,25 @@ static int reload_config(enum channelreloadreason reason)
ast_log(LOG_WARNING, "'%s' is not a valid maxforwards value at line %d. Using default.\n", v->value, v->lineno);
sip_cfg.default_max_forwards = DEFAULT_MAX_FORWARDS;
}
+ } else if (!strcasecmp(v->name, "subscribe_network_change_event")) {
+ if (ast_true(v->value)) {
+ subscribe_network_change = 1;
+ } else if (ast_false(v->value)) {
+ subscribe_network_change = 0;
+ } else {
+ ast_log(LOG_WARNING, "subscribe_network_change_event value %s is not valid at line %d.\n", v->value, v->lineno);
+ }
} else if (!strcasecmp(v->name, "snom_aoc_enabled")) {
ast_set2_flag(&global_flags[2], ast_true(v->value), SIP_PAGE3_SNOM_AOC);
}
}
+ if (subscribe_network_change) {
+ network_change_event_subscribe();
+ } else {
+ network_change_event_unsubscribe();
+ }
+
if (global_t1 < global_t1min) {
ast_log(LOG_WARNING, "'t1min' (%d) cannot be greater than 't1timer' (%d). Resetting 't1timer' to the value of 't1min'\n", global_t1min, global_t1);
global_t1 = global_t1min;
@@ -26895,15 +26946,8 @@ static int reload_config(enum channelreloadreason reason)
ast_set_qos(sipsock, global_tos_sip, global_cos_sip, "SIP");
}
}
- }
- if (stunaddr.sin_addr.s_addr != 0) {
- ast_debug(1, "stun to %s:%d\n",
- ast_inet_ntoa(stunaddr.sin_addr) , ntohs(stunaddr.sin_port));
- ast_sockaddr_to_sin(&externaddr, &externaddr_sin);
- ast_stun_request(sipsock, &stunaddr,
- NULL, &externaddr_sin);
- ast_debug(1, "STUN sees us at %s\n",
- ast_sockaddr_stringify(&externaddr));
+ } else {
+ ast_set_qos(sipsock, global_tos_sip, global_cos_sip, "SIP");
}
ast_mutex_unlock(&netlock);
@@ -27694,9 +27738,8 @@ static int sip_do_reload(enum channelreloadreason reason)
start_poke = time(0);
/* Prune peers who still are supposed to be deleted */
- ao2_t_callback(peers, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, peer_is_marked, NULL,
- "callback to remove marked peers");
-
+ unlink_marked_peers_from_tables();
+
ast_debug(4, "--------------- Done destroying pruned peers\n");
/* Send qualify (OPTIONS) to all peers */
@@ -28314,6 +28357,7 @@ static int load_module(void)
sip_register_tests();
+ network_change_event_subscribe();
return AST_MODULE_LOAD_SUCCESS;
}
@@ -28326,6 +28370,8 @@ static int unload_module(void)
struct ast_context *con;
struct ao2_iterator i;
+ network_change_event_unsubscribe();
+
ast_sched_dump(sched);
/* First, take us out of the channel type list */
diff --git a/channels/sig_analog.c b/channels/sig_analog.c
index 762169f87..90e8628e8 100644
--- a/channels/sig_analog.c
+++ b/channels/sig_analog.c
@@ -2471,7 +2471,7 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
ast_debug(1, "Got event %s(%d) on channel %d (index %d)\n", analog_event2str(res), res, p->channel, index);
if (res & (ANALOG_EVENT_PULSEDIGIT | ANALOG_EVENT_DTMFUP)) {
- analog_set_pulsedial(p, (res & ANALOG_EVENT_PULSEDIGIT));
+ analog_set_pulsedial(p, (res & ANALOG_EVENT_PULSEDIGIT) ? 1 : 0);
ast_debug(1, "Detected %sdigit '%c'\n", (res & ANALOG_EVENT_PULSEDIGIT) ? "pulse ": "", res & 0xff);
analog_confmute(p, 0);
p->subs[index].f.frametype = AST_FRAME_DTMF_END;
@@ -3581,11 +3581,9 @@ void *analog_handle_init_event(struct analog_pvt *i, int event)
}
break;
case ANALOG_EVENT_REMOVED: /* destroy channel, will actually do so in do_monitor */
- ast_log(LOG_NOTICE,
- "Got DAHDI_EVENT_REMOVED. Destroying channel %d\n",
- i->channel);
- return i->chan_pvt;
- break;
+ ast_log(LOG_NOTICE, "Got DAHDI_EVENT_REMOVED. Destroying channel %d\n",
+ i->channel);
+ return i->chan_pvt;
case ANALOG_EVENT_NEONMWI_ACTIVE:
analog_handle_notify_message(NULL, i, -1, ANALOG_EVENT_NEONMWI_ACTIVE);
break;
diff --git a/channels/sig_analog.h b/channels/sig_analog.h
index ff62ed52d..d88e622e7 100644
--- a/channels/sig_analog.h
+++ b/channels/sig_analog.h
@@ -271,6 +271,7 @@ struct analog_pvt {
unsigned int transfer:1;
unsigned int transfertobusy:1; /*!< allow flash-transfers to busy channels */
unsigned int use_callerid:1; /*!< Whether or not to use caller id on this channel */
+ unsigned int callwaitingcallerid:1;
const struct ast_channel_tech *chan_tech;
/*!
* \brief TRUE if distinctive rings are to be detected.
@@ -287,7 +288,6 @@ struct analog_pvt {
int polarityonanswerdelay;
int stripmsd;
enum analog_cid_start cid_start;
- int callwaitingcallerid;
char mohsuggest[MAX_MUSICCLASS];
char cid_num[AST_MAX_EXTENSION];
char cid_name[AST_MAX_EXTENSION];
diff --git a/channels/sig_pri.c b/channels/sig_pri.c
index 6878d81e8..c42799624 100644
--- a/channels/sig_pri.c
+++ b/channels/sig_pri.c
@@ -5917,7 +5917,20 @@ int sig_pri_call(struct sig_pri_chan *p, struct ast_channel *ast, char *rdest, i
l = NULL;
n = NULL;
if (!p->hidecallerid) {
- l = ast->connected.id.number.valid ? ast->connected.id.number.str : NULL;
+ if (ast->connected.id.number.valid) {
+ /* If we get to the end of this loop without breaking, there's no
+ * calleridnum. This is done instead of testing for "unknown" or
+ * the thousands of other ways that the calleridnum could be
+ * invalid. */
+ for (l = ast->connected.id.number.str; l && *l; l++) {
+ if (strchr("0123456789", *l)) {
+ l = ast->connected.id.number.str;
+ break;
+ }
+ }
+ } else {
+ l = NULL;
+ }
if (!p->hidecalleridname) {
n = ast->connected.id.name.valid ? ast->connected.id.name.str : NULL;
}
@@ -6316,7 +6329,7 @@ int sig_pri_indicate(struct sig_pri_chan *p, struct ast_channel *chan, int condi
case AST_CONTROL_PROGRESS:
ast_debug(1,"Received AST_CONTROL_PROGRESS on %s\n",chan->name);
sig_pri_set_digital(p, 0); /* Digital-only calls isn't allowing any inband progress messages */
- if (!p->progress && p->pri && !p->outgoing && !p->no_b_channel) {
+ if (!p->progress && !p->alerting && p->pri && !p->outgoing && !p->no_b_channel) {
if (p->pri->pri) {
if (!pri_grab(p, p->pri)) {
#ifdef HAVE_PRI_PROG_W_CAUSE
diff --git a/channels/sig_pri.h b/channels/sig_pri.h
index f57d8f700..5a770ce0b 100644
--- a/channels/sig_pri.h
+++ b/channels/sig_pri.h
@@ -295,8 +295,8 @@ struct sig_pri_span {
int fds[SIG_PRI_NUM_DCHANS]; /*!< FD's for d-channels */
#if defined(HAVE_PRI_AOC_EVENTS)
- int aoc_passthrough_flag; /*!< Represents what AOC messages (S,D,E) are allowed to pass-through */
- int aoce_delayhangup:1; /*!< defines whether the aoce_delayhangup option is enabled or not */
+ int aoc_passthrough_flag; /*!< Represents what AOC messages (S,D,E) are allowed to pass-through */
+ unsigned int aoce_delayhangup:1; /*!< defines whether the aoce_delayhangup option is enabled or not */
#endif /* defined(HAVE_PRI_AOC_EVENTS) */
#if defined(HAVE_PRI_SERVICE_MESSAGES)
diff --git a/channels/sip/config_parser.c b/channels/sip/config_parser.c
index 659e8cecc..0ab9ed769 100644
--- a/channels/sip/config_parser.c
+++ b/channels/sip/config_parser.c
@@ -661,16 +661,18 @@ int sip_parse_host(char *line, int lineno, char **hostname, int *portnum, enum s
else
line = *hostname;
- if ((port = strrchr(line, ':'))) {
- *port++ = '\0';
+ if (ast_sockaddr_split_hostport(line, hostname, &port, 0) == 0) {
+ ast_log(LOG_WARNING, "Cannot parse host '%s' on line %d of sip.conf.\n",
+ line, lineno);
+ return -1;
+ }
+ if (port) {
if (!sscanf(port, "%5u", portnum)) {
ast_log(LOG_NOTICE, "'%s' is not a valid port number on line %d of sip.conf. using default.\n", port, lineno);
port = NULL;
}
- }
-
- if (!port) {
+ } else {
if (*transport & SIP_TRANSPORT_TLS) {
*portnum = STANDARD_TLS_PORT;
} else {
diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h
index 0e207ca23..cf3c0da69 100644
--- a/channels/sip/include/sip.h
+++ b/channels/sip/include/sip.h
@@ -214,7 +214,7 @@
#define DEFAULT_MATCHEXTERNADDRLOCALLY FALSE /*!< Match extern IP locally default setting */
#define DEFAULT_QUALIFY FALSE /*!< Don't monitor devices */
#define DEFAULT_CALLEVENTS FALSE /*!< Extra manager SIP call events */
-#define DEFAULT_ALWAYSAUTHREJECT FALSE /*!< Don't reject authentication requests always */
+#define DEFAULT_ALWAYSAUTHREJECT TRUE /*!< Don't reject authentication requests always */
#define DEFAULT_REGEXTENONQUALIFY FALSE
#define DEFAULT_T1MIN 100 /*!< 100 MS for minimal roundtrip time */
#define DEFAULT_MAX_CALL_BITRATE (384) /*!< Max bitrate for video */
diff --git a/configs/cdr.conf.sample b/configs/cdr.conf.sample
index 3866ab1fa..b0c38c6cf 100644
--- a/configs/cdr.conf.sample
+++ b/configs/cdr.conf.sample
@@ -29,6 +29,22 @@
; channel.)
;unanswered = no
+; Normally, CDR's are not closed out until after all extensions are finished
+; executing. By enabling this option, the CDR will be ended before executing
+; the "h" extension so that CDR values such as "end" and "billsec" may be
+; retrieved inside of of this extension. The default value is "no".
+;endbeforehexten=no
+
+; Normally, the 'billsec' field logged to the backends (text files or databases)
+; is simply the end time (hangup time) minus the answer time in seconds. Internally,
+; asterisk stores the time in terms of microseconds and seconds. By setting
+; initiatedseconds to 'yes', you can force asterisk to report any seconds
+; that were initiated (a sort of round up method). Technically, this is
+; when the microsecond part of the end time is greater than the microsecond
+; part of the answer time, then the billsec time is incremented one second.
+; The default value is "no".
+;initiatedseconds=no
+
; Define the CDR batch mode, where instead of posting the CDR at the end of
; every call, the data will be stored in a buffer to help alleviate load on the
; asterisk server. Default is "no".
@@ -65,21 +81,6 @@
; is "yes".
;safeshutdown=yes
-; Normally, CDR's are not closed out until after all extensions are finished
-; executing. By enabling this option, the CDR will be ended before executing
-; the "h" extension so that CDR values such as "end" and "billsec" may be
-; retrieved inside of of this extension.
-;endbeforehexten=no
-
-; Normally, the 'billsec' field logged to the backends (text files or databases)
-; is simply the end time (hangup time) minus the answer time in seconds. Internally,
-; asterisk stores the time in terms of microseconds and seconds. By setting
-; initiatedseconds to 'yes', you can force asterisk to report any seconds
-; that were initiated (a sort of round up method). Technically, this is
-; when the microsecond part of the end time is greater than the microsecond
-; part of the answer time, then the billsec time is incremented one second.
-;initiatedseconds=no
-
;
;
; CHOOSING A CDR "BACKEND" (what kind of output to generate)
diff --git a/configs/cel_adaptive_odbc.conf.sample b/configs/cel_adaptive_odbc.conf.sample
index 01702d192..5f21326df 100644
--- a/configs/cel_adaptive_odbc.conf.sample
+++ b/configs/cel_adaptive_odbc.conf.sample
@@ -61,7 +61,7 @@
; peeraccount
; uniqueid
; linkedid
-; amaflag (an int)
+; amaflags (an int)
; userfield
; peer
diff --git a/configs/cel_custom.conf.sample b/configs/cel_custom.conf.sample
index 4c5fbb093..37491a48b 100644
--- a/configs/cel_custom.conf.sample
+++ b/configs/cel_custom.conf.sample
@@ -17,5 +17,15 @@
; in the cel-custom directory under your Asterisk logs directory.
;
+;
+; Within a mapping, use the CALLERID() and CHANNEL() functions to retrieve
+; details from the CEL event. There are also a few variables created by this
+; module that can be used in a mapping:
+;
+; eventtype - The name of the CEL event.
+; eventtime - The timestamp of the CEL event.
+; eventextra - Extra data included with this CEL event, typically along with
+; an event of type USER_DEFINED from CELGenUserEvent().
+;
[mappings]
-;Master.csv => "${eventtype}","${eventtime}","${CALLERID(name)}","${CALLERID(num)}","${CALLERID(ANI)}","${CALLERID(RDNIS)}","${CALLERID(DNID)}","${CHANNEL(exten)}","${CHANNEL(context)}","${CHANNEL(channame)}","${CHANNEL(appname)}","${CHANNEL(appdata)}","${CHANNEL(amaflags)}","${CHANNEL(accountcode)}","${CHANNEL(uniqueid)}","${CHANNEL(linkedid)}","${CHANNEL(peer)}","${CHANNEL(userfield)}"
+;Master.csv => ${CSV_QUOTE(${eventtype})},${CSV_QUOTE(${eventtime})},${CSV_QUOTE(${CALLERID(name)})},${CSV_QUOTE(${CALLERID(num)})},${CSV_QUOTE(${CALLERID(ANI)})},${CSV_QUOTE(${CALLERID(RDNIS)})},${CSV_QUOTE(${CALLERID(DNID)})},${CSV_QUOTE(${CHANNEL(exten)})},${CSV_QUOTE(${CHANNEL(context)})},${CSV_QUOTE(${CHANNEL(channame)})},${CSV_QUOTE(${CHANNEL(appname)})},${CSV_QUOTE(${CHANNEL(appdata)})},${CSV_QUOTE(${CHANNEL(amaflags)})},${CSV_QUOTE(${CHANNEL(accountcode)})},${CSV_QUOTE(${CHANNEL(uniqueid)})},${CSV_QUOTE(${CHANNEL(linkedid)})},${CSV_QUOTE(${CHANNEL(peer)})},${CSV_QUOTE(${CHANNEL(userfield)})},${CSV_QUOTE(${eventextra})}
diff --git a/configs/iax.conf.sample b/configs/iax.conf.sample
index b516d4b85..73ae78afb 100644
--- a/configs/iax.conf.sample
+++ b/configs/iax.conf.sample
@@ -244,6 +244,15 @@ forcejitterbuffer=no
;
;register => FWDNumber:passwd@iax.fwdnet.net
;
+; Through the use of the res_stun_monitor module, Asterisk has the ability to detect when the
+; perceived external network address has changed. When the stun_monitor is installed and
+; configured, chan_iax will renew all outbound registrations when the monitor detects any sort
+; of network change has occurred. By default this option is enabled, but only takes effect once
+; res_stun_monitor is configured. If res_stun_monitor is enabled and you wish to not
+; generate all outbound registrations on a network change, use the option below to disable
+; this feature.
+;
+; subscribe_network_change_event = yes ; on by default
;
; You can disable authentication debugging to reduce the amount of
; debugging traffic.
diff --git a/configs/res_stun_monitor.conf.sample b/configs/res_stun_monitor.conf.sample
new file mode 100644
index 000000000..9237799c5
--- /dev/null
+++ b/configs/res_stun_monitor.conf.sample
@@ -0,0 +1,22 @@
+;
+; Configuration file for the res_stun_monitor module
+;
+; The res_stun_monitor module sends STUN requests to a configured STUN server
+; periodically. If the monitor detects a change in the external ip or port
+; provided by the STUN server an event is sent out internally within Asterisk
+; to alert all listeners to that event of the change.
+
+; The current default listeners for the netork change event include chan_sip
+; and chan_iax. Both of these channel drivers by default react to this event
+; by renewing all outbound registrations. This allows the endpoints Asterisk
+; is registering with to become aware of the address change and know the new
+; location.
+;
+[general]
+;
+; ---- STUN Server configuration ---
+; Setting the 'stunaddr' option to a valid address enables the stun monitor.
+;
+; stunaddr = mystunserver.com ; address of the stun server to query.
+; stunrefresh = 30 ; number of seconds between stun refreshes. default is 30
+;
diff --git a/configs/say.conf.sample b/configs/say.conf.sample
index 670e3944e..8148422f4 100644
--- a/configs/say.conf.sample
+++ b/configs/say.conf.sample
@@ -106,16 +106,25 @@ mode=old ; method for playing numbers and dates
_[n]um:X00 => num:${SAY:0:1}, digits/hundred
_[n]um:XXX => num:${SAY:0:1}, digits/hundred, num:${SAY:1}
+ _[n]um:X000 => num:${SAY:0:1}, digits/thousand
_[n]um:XXXX => num:${SAY:0:1}, digits/thousand, num:${SAY:1}
+ _[n]um:XX000 => num:${SAY:0:2}, digits/thousand
_[n]um:XXXXX => num:${SAY:0:2}, digits/thousand, num:${SAY:2}
+ _[n]um:XXX000 => num:${SAY:0:3}, digits/thousand
_[n]um:XXXXXX => num:${SAY:0:3}, digits/thousand, num:${SAY:3}
+ _[n]um:X000000 => num:${SAY:0:1}, digits/million
_[n]um:XXXXXXX => num:${SAY:0:1}, digits/million, num:${SAY:1}
+ _[n]um:XX000000 => num:${SAY:0:2}, digits/million
_[n]um:XXXXXXXX => num:${SAY:0:2}, digits/million, num:${SAY:2}
+ _[n]um:XXX000000 => num:${SAY:0:3}, digits/million
_[n]um:XXXXXXXXX => num:${SAY:0:3}, digits/million, num:${SAY:3}
+ _[n]um:X000000000 => num:${SAY:0:1}, digits/billion
_[n]um:XXXXXXXXXX => num:${SAY:0:1}, digits/billion, num:${SAY:1}
+ _[n]um:XX000000000 => num:${SAY:0:2}, digits/billion
_[n]um:XXXXXXXXXXX => num:${SAY:0:2}, digits/billion, num:${SAY:2}
+ _[n]um:XXX000000000 => num:${SAY:0:3}, digits/billion
_[n]um:XXXXXXXXXXXX => num:${SAY:0:3}, digits/billion, num:${SAY:3}
; enumeration
@@ -123,7 +132,8 @@ mode=old ; method for playing numbers and dates
_e[n]um:1X => digits/h-${SAY}
_e[n]um:[2-9]0 => digits/h-${SAY}
_e[n]um:[2-9][1-9] => num:${SAY:0:1}0, digits/h-${SAY:1}
- _e[n]um:[1-9]XX => num:${SAY:0:1}, digits/hundred, enum:${SAY:1}
+ _e[n]um:[1-9]00 => num:${SAY:0:1}, digits/h-hundred
+ _e[n]um:[1-9]XX => num:${SAY:0:1}, digits/h-hundred, enum:${SAY:1}
[en_GB](date-base,digit-base,en-base)
_[n]um:XXX => num:${SAY:0:1}, digits/hundred, vm-and, num:${SAY:1}
@@ -139,16 +149,31 @@ mode=old ; method for playing numbers and dates
_[n]um:[2-9]00 => num:${SAY:0:1}, digits/hundred
_[n]um:[2-9]XX => num:${SAY:0:1}, digits/hundred, num:${SAY:1}
+ _[n]um:1000 => digits/thousand
_[n]um:1XXX => digits/thousand, num:${SAY:1}
+ _[n]um:[2-9]000 => num:${SAY:0:1}, digits/thousands
_[n]um:[2-9]XXX => num:${SAY:0:1}, digits/thousands, num:${SAY:1}
+ _[n]um:XX000 => num:${SAY:0:2}, digits/thousands
_[n]um:XXXXX => num:${SAY:0:2}, digits/thousands, num:${SAY:2}
+ _[n]um:XXX000 => num:${SAY:0:3}, digits/thousands
_[n]um:XXXXXX => num:${SAY:0:3}, digits/thousands, num:${SAY:3}
+ _[n]um:1000000 => num:${SAY:0:1}, digits/million
_[n]um:1XXXXXX => num:${SAY:0:1}, digits/million, num:${SAY:1}
+ _[n]um:[2-9]000000 => num:${SAY:0:1}, digits/millions
_[n]um:[2-9]XXXXXX => num:${SAY:0:1}, digits/millions, num:${SAY:1}
+ _[n]um:XX000000 => num:${SAY:0:2}, digits/millions
_[n]um:XXXXXXXX => num:${SAY:0:2}, digits/millions, num:${SAY:2}
+ _[n]um:XXX000000 => num:${SAY:0:3}, digits/millions
_[n]um:XXXXXXXXX => num:${SAY:0:3}, digits/millions, num:${SAY:3}
+ _[n]um:X000000000 => num:${SAY:0:1}, digits/billion
+ _[n]um:XXXXXXXXXX => num:${SAY:0:1}, digits/billion, num:${SAY:1}
+ _[n]um:XX000000000 => num:${SAY:0:2}, digits/billion
+ _[n]um:XXXXXXXXXXX => num:${SAY:0:2}, digits/billion, num:${SAY:2}
+ _[n]um:XXX000000000 => num:${SAY:0:3}, digits/billion
+ _[n]um:XXXXXXXXXXXX => num:${SAY:0:3}, digits/billion, num:${SAY:3}
+
_datetime::. => date:AdBY 'digits/at' IMp:${SAY}
_date::. => date:AdBY:${SAY}
_time::. => date:IMp:${SAY}
@@ -168,14 +193,23 @@ mode=old ; method for playing numbers and dates
_[n]um:1XX => digits/ein, digits/hundred, num:${SAY:1}
_[n]um:[2-9]00 => digits/${SAY:0:1}, digits/hundred
_[n]um:[2-9]XX => digits/${SAY:0:1}, digits/hundred, num:${SAY:1}
+
+ _[n]um:1000 => digits/ein, digits/thousand
_[n]um:1XXX => digits/ein, digits/thousand, num:${SAY:1}
+ _[n]um:[2-9]000 => digits/${SAY:0:1}, digits/thousand
_[n]um:[2-9]XXX => digits/${SAY:0:1}, digits/thousand, num:${SAY:1}
+ _[n]um:XX000 => num:${SAY:0:2}, digits/thousand
_[n]um:XXXXX => num:${SAY:0:2}, digits/thousand, num:${SAY:2}
- _[n]um:X00XXX => digits/${SAY:0:1}, digits/hundred, digits/thousand, num:${SAY:3}
- _[n]um:XXXXXX => digits/${SAY:0:1}, digits/hundred, num:${SAY:1}
+ _[n]um:XXX000 => num:${SAY:0:3}, digits/thousand
+ _[n]um:XXXXXX => num:${SAY:0:3}, digits/thousand, num:${SAY:1}
+
+ _[n]um:1000000 => digits/eine, digits/million
_[n]um:1XXXXXX => digits/eine, digits/million, num:${SAY:1}
+ _[n]um:[2-9]000000 => digits/${SAY:0:1}, digits/millions
_[n]um:[2-9]XXXXXX => digits/${SAY:0:1}, digits/millions, num:${SAY:1}
+ _[n]um:XX000000 => num:${SAY:0:2}, digits/millions
_[n]um:XXXXXXXX => num:${SAY:0:2}, digits/millions, num:${SAY:2}
+ _[n]um:XXX000000 => num:${SAY:0:3}, digits/millions
_[n]um:XXXXXXXXX => num:${SAY:0:3}, digits/millions, num:${SAY:3}
_datetime::. => date:AdBY 'digits/at' IMp:${SAY}
@@ -191,16 +225,25 @@ mode=old ; method for playing numbers and dates
_[n]um:[3-9][1-9] => digits/${SAY:0:1}0, num:${SAY:1}
_[n]um:XXX => num:${SAY:0:1}, digits/hundred, num:${SAY:1}
+ _[n]um:X000 => num:${SAY:0:1}, digits/thousand
_[n]um:XXXX => num:${SAY:0:1}, digits/thousand, num:${SAY:1}
+ _[n]um:XX000 => num:${SAY:0:2}, digits/thousand
_[n]um:XXXXX => num:${SAY:0:2}, digits/thousand, num:${SAY:2}
+ _[n]um:XXX000 => num:${SAY:0:3}, digits/thousand
_[n]um:XXXXXX => num:${SAY:0:3}, digits/thousand, num:${SAY:3}
+ _[n]um:X000000 => num:${SAY:0:1}, digits/million
_[n]um:XXXXXXX => num:${SAY:0:1}, digits/million, num:${SAY:1}
+ _[n]um:XX000000 => num:${SAY:0:2}, digits/million
_[n]um:XXXXXXXX => num:${SAY:0:2}, digits/million, num:${SAY:2}
+ _[n]um:XXX000000 => num:${SAY:0:3}, digits/million
_[n]um:XXXXXXXXX => num:${SAY:0:3}, digits/million, num:${SAY:3}
+ _[n]um:X000000000 => num:${SAY:0:1}, digits/billion
_[n]um:XXXXXXXXXX => num:${SAY:0:1}, digits/billion, num:${SAY:1}
+ _[n]um:XX000000000 => num:${SAY:0:2}, digits/billion
_[n]um:XXXXXXXXXXX => num:${SAY:0:2}, digits/billion, num:${SAY:2}
+ _[n]um:XXX000000000 => num:${SAY:0:3}, digits/billion
_[n]um:XXXXXXXXXXXX => num:${SAY:0:3}, digits/billion, num:${SAY:3}
_datetime::. => date:YBdA k 'ora' M 'perc':${SAY}
@@ -222,14 +265,22 @@ mode=old ; method for playing numbers and dates
_[n]um:[2-9]00 => num:${SAY:0:1}, digits/hundred
_[n]um:[2-9]XX => num:${SAY:0:1}, digits/hundred, num:${SAY:1}
+ _[n]um:1000 => digits/thousand
_[n]um:1XXX => digits/thousand, num:${SAY:1}
+ _[n]um:[2-9]000 => num:${SAY:0:1}, digits/thousand
_[n]um:[2-9]XXX => num:${SAY:0:1}, digits/thousand, num:${SAY:1}
+ _[n]um:XX000 => num:${SAY:0:2}, digits/thousand
_[n]um:XXXXX => num:${SAY:0:2}, digits/thousand, num:${SAY:2}
+ _[n]um:XXX000 => num:${SAY:0:3}, digits/thousand
_[n]um:XXXXXX => num:${SAY:0:3}, digits/thousand, num:${SAY:3}
+ _[n]um:1000000 => num:${SAY:0:1}, digits/million
_[n]um:1XXXXXX => num:${SAY:0:1}, digits/million, num:${SAY:1}
+ _[n]um:[2-9]000000 => num:${SAY:0:1}, digits/million
_[n]um:[2-9]XXXXXX => num:${SAY:0:1}, digits/million, num:${SAY:1}
+ _[n]um:XX000000 => num:${SAY:0:2}, digits/million
_[n]um:XXXXXXXX => num:${SAY:0:2}, digits/million, num:${SAY:2}
+ _[n]um:XXX000000 => num:${SAY:0:3}, digits/million
_[n]um:XXXXXXXXX => num:${SAY:0:3}, digits/million, num:${SAY:3}
_datetime::. => date:AdBY 'digits/at' H 'hours' M 'perc':${SAY}
@@ -276,3 +327,50 @@ mode=old ; method for playing numbers and dates
_date::. => date:Ad 'letters/d' B 'letters/d' Y:${SAY}
_time::. => date:HMp:${SAY}
+[da](date-base,digit-base)
+ _[n]um:0. => num:${SAY:1}
+ _[n]um:X => digits/${SAY}
+ _[n]um:1X => digits/${SAY}
+ _[n]um:[2-9]0 => digits/${SAY}
+ _[n]um:[2-9][1-9] => digits/${SAY:1}-and, digits/${SAY:0:1}0
+ _[n]um:100 => digits/1N, digits/hundred
+ _[n]um:1XX => digits/1N, digits/hundred, num:${SAY:1}
+ _[n]um:[2-9]00 => digits/${SAY:0:1}, digits/hundred
+ _[n]um:[2-9]XX => digits/${SAY:0:1}, digits/hundred, num:${SAY:1}
+
+ _[n]um:1000 => digits/1N, digits/thousand
+ _[n]um:1XXX => digits/1N, digits/thousand, num:${SAY:1}
+ _[n]um:[2-9]000 => digits/${SAY:0:1}, digits/thousand
+ _[n]um:[2-9]XXX => digits/${SAY:0:1}, digits/thousand, num:${SAY:1}
+ _[n]um:XX000 => num:${SAY:0:2}, digits/thousand
+ _[n]um:XXXXX => num:${SAY:0:2}, digits/thousand, num:${SAY:2}
+ _[n]um:XXX000 => num:${SAY:0:3}, digits/thousand
+ _[n]um:XXXXXX => num:${SAY:0:3}, digits/thousand, num:${SAY:3}
+
+ _[n]um:X000000 => digits/${SAY:0:1}, digits/million
+ _[n]um:XXXXXXX => digits/${SAY:0:1}, digits/million, num:${SAY:1}
+ _[n]um:XX000000 => num:${SAY:0:2}, digits/millions
+ _[n]um:XXXXXXXX => num:${SAY:0:2}, digits/millions, num:${SAY:2}
+ _[n]um:XXX000000 => num:${SAY:0:3}, digits/millions
+ _[n]um:XXXXXXXXX => num:${SAY:0:3}, digits/millions, num:${SAY:3}
+
+ _[n]um:X000000000 => num:${SAY:0:1}, digits/billion
+ _[n]um:XXXXXXXXXX => num:${SAY:0:1}, digits/billion, num:${SAY:1}
+ _[n]um:XX000000000 => num:${SAY:0:2}, digits/billion
+ _[n]um:XXXXXXXXXXX => num:${SAY:0:2}, digits/billion, num:${SAY:2}
+ _[n]um:XXX000000000 => num:${SAY:0:3}, digits/billion
+ _[n]um:XXXXXXXXXXXX => num:${SAY:0:3}, digits/billion, num:${SAY:3}
+
+ _datetime::. => date:AdBY 'digits/at' kM:${SAY}
+ _date::. => date:AdBY:${SAY}
+ _time::. => date:HM:${SAY}
+
+ ; enumeration
+ _e[n]um:X => digits/h-${SAY}
+ _e[n]um:1X => digits/h-${SAY}
+ _e[n]um:[2-9]0 => digits/h-${SAY}
+ _e[n]um:[2-9][1-9] => digits/${SAY:1}-and, digits/h-${SAY:0:1}0
+ _e[n]um:100 => digits/1N, digits/h-hundred
+ _e[n]um:1XX => digits/1N, digits/h-hundred, enum:${SAY:1}
+ _e[n]um:[2-9]00 => num:${SAY:0:1}, digits/h-hundred
+ _e[n]um:[2-9]XX => num:${SAY:0:1}, digits/h-hundred, enum:${SAY:1}
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index 01fd29b00..287e3c52b 100644
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -144,6 +144,8 @@ allowoverlap=no ; Disable overlap dialing support. (Default is y
; d) Listen on the IPv4 and IPv6 wildcards. Example: bindaddr=::
; (You can choose independently for UDP, TCP, and TLS, by specifying different values for
; "udpbindaddr", "tcpbindaddr", and "tlsbindaddr".)
+; (Note that using bindaddr=:: will show only a single IPv6 socket in netstat.
+; IPv4 is supported at the same time using IPv4-mapped IPv6 addresses.)
;
; You may optionally add a port number. (The default is port 5060 for UDP and TCP, 5061
; for TLS).
@@ -253,6 +255,18 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
; Message-Account in the MWI notify message
; defaults to "asterisk"
+; Codec negotiation
+;
+; When Asterisk is receiving a call, the codec will initially be set to the
+; first codec in the allowed codecs defined for the user receiving the call
+; that the caller also indicates that it supports. But, after the caller
+; starts sending RTP, Asterisk will switch to using whatever codec the caller
+; is sending.
+;
+; When Asterisk is placing a call, the codec used will be the first codec in
+; the allowed codecs that the callee indicates that it supports. Asterisk will
+; *not* switch to whatever codec the callee is sending.
+;
;preferred_codec_only=yes ; Respond to a SIP invite with the single most preferred codec
; rather than advertising all joint codec capabilities. This
; limits the other side's codec choice to exactly what we prefer.
@@ -354,6 +368,7 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
; instead of letting the requester know whether there was
; a matching user or peer for their request. This reduces
; the ability of an attacker to scan for valid SIP usernames.
+ ; This option is set to "yes" by default.
;g726nonstandard = yes ; If the peer negotiates G726-32 audio, use AAL2 packing
; order instead of RFC3551 packing order (this is required
@@ -364,6 +379,10 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
;outboundproxy=proxy.provider.domain:8080 ; send outbound signaling to this proxy, not directly to the devices
;outboundproxy=proxy.provider.domain,force ; Send ALL outbound signalling to proxy, ignoring route: headers
;outboundproxy=tls://proxy.provider.domain ; same as '=proxy.provider.domain' except we try to connect with tls
+;outboundproxy=192.0.2.1 ; IPv4 address literal (default port is 5060)
+;outboundproxy=2001:db8::1 ; IPv6 address literal (default port is 5060)
+;outboundproxy=192.168.0.2.1:5062 ; IPv4 address literal with explicit port
+;outboundproxy=[2001:db8::1]:5062 ; IPv6 address literal with explicit port
; ; (could also be tcp,udp) - defining transports on the proxy line only
; ; applies for the global proxy, otherwise use the transport= option
;matchexternaddrlocally = yes ; Only substitute the externaddr or externhost setting if it matches
@@ -720,31 +739,18 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
; externhost=foo.dyndns.net ; refreshed periodically
; externrefresh=180 ; change the refresh interval
;
-; c. "stunaddr = stun.server[:port]" queries the STUN server specified
-; as an argument to obtain the external address/port.
-; Queries are also sent periodically every "externrefresh" seconds
-; (as a side effect, sending the query also acts as a keepalive for
-; the state entry on the nat box):
-;
-; stunaddr = foo.stun.com:3478
-; externrefresh = 15
-;
-; NOTE: STUN is only implemented for IPv4.
-;
; Note that at the moment all these mechanism work only for the SIP socket.
-; The IP address discovered with externaddr/externhost/STUN is reused for
+; The IP address discovered with externaddr/externhost is reused for
; media sessions as well, but the port numbers are not remapped so you
; may still experience problems.
;
; NOTE 1: in some cases, NAT boxes will use different port numbers in
; the internal<->external mapping. In these cases, the "externaddr" and
-; "externhost" might not help you configure addresses properly, and you
-; really need to use STUN.
+; "externhost" might not help you configure addresses properly.
;
; NOTE 2: when using "externaddr" or "externhost", the address part is
-; also used as the external address for media sessions. Even if you
-; use "stunaddr", STUN queries will be sent only from the SIP port,
-; not from media sockets. Thus, the port information in the SDP may be wrong!
+; also used as the external address for media sessions. Thus, the port
+; information in the SDP may be wrong!
;
; In addition to the above, Asterisk has an additional "nat" parameter to
; address NAT-related issues in incoming SIP or media sessions.
@@ -776,6 +782,16 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
; can not be set per-user or per-peer.
;
; media_address = 172.16.42.1
+;
+; Through the use of the res_stun_monitor module, Asterisk has the ability to detect when the
+; perceived external network address has changed. When the stun_monitor is installed and
+; configured, chan_sip will renew all outbound registrations when the monitor detects any sort
+; of network change has occurred. By default this option is enabled, but only takes effect once
+; res_stun_monitor is configured. If res_stun_monitor is enabled and you wish to not
+; generate all outbound registrations on a network change, use the option below to disable
+; this feature.
+;
+; subscribe_network_change_event = yes ; on by default
;----------------------------------- MEDIA HANDLING --------------------------------
; By default, Asterisk tries to re-invite media streams to an optimal path. If there's
diff --git a/configure b/configure
index 655adc785..571e71106 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
#! /bin/sh
-# From configure.ac Revision: 279658 .
+# From configure.ac Revision: 280020 .
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.65 for asterisk trunk.
#
@@ -26148,6 +26148,61 @@ fi
+if test "$PBX_SRTP" = "1";
+then
+ saved_libs="${LIBS}"
+ saved_ldflags="${LDFLAGS}"
+ LIBS="${LIBS} -lsrtp"
+ LDFLAGS="${LDFLAGS} -shared -fPIC"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the ability of -lsrtp to be linked in a shared object" >&5
+$as_echo_n "checking for the ability of -lsrtp to be linked in a shared object... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ #include <srtp/srtp.h>
+int
+main ()
+{
+srtp_init();
+
+ ;
+ return 0;
+}
+
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: ***" >&5
+$as_echo "$as_me: ***" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: *** libsrtp could not be linked as a shared object" >&5
+$as_echo "$as_me: *** libsrtp could not be linked as a shared object" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: *** try compiling libsrtp manually and configuring with" >&5
+$as_echo "$as_me: *** try compiling libsrtp manually and configuring with" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: *** ./configure CFLAGS=-fPIC --prefix=/usr" >&5
+$as_echo "$as_me: *** ./configure CFLAGS=-fPIC --prefix=/usr" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: *** replacing /usr with the prefix of your choice" >&5
+$as_echo "$as_me: *** replacing /usr with the prefix of your choice" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: ***" >&5
+$as_echo "$as_me: ***" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: *** If you do not need SRTP support re-run configure" >&5
+$as_echo "$as_me: *** If you do not need SRTP support re-run configure" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: *** with the --without-srtp option." >&5
+$as_echo "$as_me: *** with the --without-srtp option." >&6;}
+ exit 1
+
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="${saved_libs}"
+ LDFLAGS="${saved_ldflags}"
+fi
+
if test "x${PBX_GMIME}" != "x1" -a "${USE_GMIME}" != "no"; then
PBX_GMIME=0
diff --git a/configure.ac b/configure.ac
index dfc45a5e8..62e7895c3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1807,6 +1807,38 @@ fi
AST_EXT_LIB_CHECK([SRTP], [srtp], [srtp_init], [srtp/srtp.h])
+if test "$PBX_SRTP" = "1";
+then
+ saved_libs="${LIBS}"
+ saved_ldflags="${LDFLAGS}"
+ LIBS="${LIBS} -lsrtp"
+ LDFLAGS="${LDFLAGS} -shared -fPIC"
+ AC_MSG_CHECKING(for the ability of -lsrtp to be linked in a shared object)
+ AC_LINK_IFELSE(
+ [
+ AC_LANG_PROGRAM(
+ [#include <srtp/srtp.h>],
+ [srtp_init();]
+ )
+ ],
+ [ AC_MSG_RESULT(yes) ],
+ [
+ AC_MSG_RESULT(no)
+ AC_MSG_NOTICE(***)
+ AC_MSG_NOTICE(*** libsrtp could not be linked as a shared object)
+ AC_MSG_NOTICE(*** try compiling libsrtp manually and configuring with)
+ AC_MSG_NOTICE(*** ./configure CFLAGS=-fPIC --prefix=/usr)
+ AC_MSG_NOTICE(*** replacing /usr with the prefix of your choice)
+ AC_MSG_NOTICE(***)
+ AC_MSG_NOTICE(*** If you do not need SRTP support re-run configure)
+ AC_MSG_NOTICE(*** with the --without-srtp option.)
+ exit 1
+ ]
+ )
+ LIBS="${saved_libs}"
+ LDFLAGS="${saved_ldflags}"
+fi
+
AST_EXT_TOOL_CHECK([GMIME], [gmime-config], [], [], [#include <gmime/gmime.h>], [gboolean q = g_mime_check_version(0,0,0);])
AST_EXT_LIB_CHECK([HOARD], [hoard], [malloc], [])
diff --git a/doc/tex/asterisk.tex b/doc/tex/asterisk.tex
index 4934d14fc..4cf45df15 100644
--- a/doc/tex/asterisk.tex
+++ b/doc/tex/asterisk.tex
@@ -157,6 +157,9 @@ reference purposes.
\chapter{Packet Loss Concealment}
\input{plc.tex}
+\chapter{Sounds Packages}
+ \input{sounds.tex}
+
\chapter{Development}
\section{Backtrace}
\input{backtrace.tex}
diff --git a/doc/tex/sounds.tex b/doc/tex/sounds.tex
new file mode 100644
index 000000000..59091983b
--- /dev/null
+++ b/doc/tex/sounds.tex
@@ -0,0 +1,80 @@
+\section{Introduction}
+Asterisk utilizes a variety of sound prompts that are available in several file
+formats and languages. Multiple languages and formats can be installed on the
+same system, and Asterisk will utilize prompts from languages installed, and
+will automatically pick the least CPU intensive format that is available on the
+system (based on codecs in use, in additional to the codec and format modules
+installed and available).
+
+In addition to the prompts available with Asterisk, you can create your own sets
+of prompts and utilize them as well. This document will tell you how the prompts
+available for Asterisk are created so that the prompts you create can be as close
+and consistent in the quality and volume levels as those shipped with Asterisk.
+
+\section{Getting The Sounds Tools}
+The sounds tools are available in the publicly accessible repotools repository.
+You can check these tools out with Subversion via the following command:
+
+\begin{astlisting}
+\begin{verbatim}
+# svn co http://svn.asterisk.org/svn/repotools
+\end{verbatim}
+\end{astlisting}
+
+The sound tools are available in the subdirectory sound_tools/ which contains the
+following directories:
+
+\begin{itemize}
+\item audiofilter
+\item makeg722
+\item scripts
+\end{itemize}
+
+\section{About The Sounds Tools}
+The following sections will describe the sound tools in more detail and explain what
+they are used for in the sounds package creation process.
+
+\subsection{audiofilter}
+The audiofilter application is used to "tune" the sound files in such a way that
+they sound good when being used while in a compressed format. The values in the
+scripts for creating the sound files supplied in repotools is essentially a
+high-pass filter that drops out audio below 100Hz (or so).
+
+(There is an ITU specification that states for 8KHz audio that is being compressed
+frequencies below a certain threshold should be removed because they make the
+resulting compressed audio sound worse than it should.)
+
+The audiofilter application is used by the 'converter' script located in the
+scripts subdirectory of repotools/sound_tools. The values being passed to the
+audiofilter application is as follows:
+
+\begin{astlisting}
+\begin{verbatim}
+audiofilter -n 0.86916 -1.73829 0.86916 -d 1.00000 -1.74152 0.77536
+\end{verbatim}
+\end{astlisting}
+
+
+The two options -n and -d are 'numerator' and 'denominator'. Per the author,
+Jean-Marc Valin, "These values are filter coefficients (-n means numerator, -d is
+denominator) expressed in the z-transform domain. There represent an elliptic filter
+that I designed with Octave such that 'the result sounds good'."
+
+\subsection{makeg722}
+The makeg722 application is used by the 'converters' script to generate the G.722
+sound files that are shipped with Asterisk. It starts with the RAW sound files and
+then converts them to G.722.
+
+\subsection{scripts}
+The scripts folder is where all the magic happens. These are the scripts that the
+Asterisk open source team use to build the packaged audio files for the various
+formats that are distributed with Asterisk.
+
+\begin{itemize}
+\item chkcore - used to check that the contents of core-sounds-$<$lang$>$.txt are in sync
+\item chkextra - same as above, but checks the extra sound files
+\item mkcore - script used to generate the core sounds packages
+\item mkextra - script used to generate the extra sounds packages
+\item mkmoh - script used to generate the music on hold packages
+\item converters - script used to convert the master files to various formats
+\end{itemize}
diff --git a/include/asterisk/ccss.h b/include/asterisk/ccss.h
index 4dc8e5922..31073a478 100644
--- a/include/asterisk/ccss.h
+++ b/include/asterisk/ccss.h
@@ -189,6 +189,21 @@ int ast_cc_is_config_param(const char * const name);
/*!
* \since 1.8
+ * \brief Set the specified CC config params to default values.
+ *
+ * \details
+ * This is just like ast_cc_copy_config_params() and could be used in place
+ * of it if you need to set the config params to defaults instead.
+ * You are simply "copying" defaults into the destination.
+ *
+ * \param params CC config params to set to default values.
+ *
+ * \return Nothing
+ */
+void ast_cc_default_config_params(struct ast_cc_config_params *params);
+
+/*!
+ * \since 1.8
* \brief copy CCSS configuration parameters from one structure to another
*
* \details
@@ -199,8 +214,8 @@ int ast_cc_is_config_param(const char * const name);
*
* \param src The structure from which data is copied
* \param dest The structure to which data is copied
- * \retval -1 Copy failed (no way for this to happen yet)
- * \retval 0 Copy succeeded
+ *
+ * \return Nothing
*/
void ast_cc_copy_config_params(struct ast_cc_config_params *dest, const struct ast_cc_config_params *src);
diff --git a/include/asterisk/event_defs.h b/include/asterisk/event_defs.h
index 3779dac73..073d67bc6 100644
--- a/include/asterisk/event_defs.h
+++ b/include/asterisk/event_defs.h
@@ -52,8 +52,10 @@ enum ast_event_type {
AST_EVENT_CEL = 0x07,
/*! A report of a security related event (see security_events.h) */
AST_EVENT_SECURITY = 0x08,
+ /*! Used by res_stun_monitor to alert listeners to an exernal network address change. */
+ AST_EVENT_NETWORK_CHANGE = 0x09,
/*! Number of event types. This should be the last event type + 1 */
- AST_EVENT_TOTAL = 0x09,
+ AST_EVENT_TOTAL = 0x0a,
};
/*! \brief Event Information Element types */
diff --git a/include/asterisk/netsock2.h b/include/asterisk/netsock2.h
index 24330d236..73c57c5d2 100644
--- a/include/asterisk/netsock2.h
+++ b/include/asterisk/netsock2.h
@@ -233,6 +233,26 @@ static inline char *ast_sockaddr_stringify_port(const struct ast_sockaddr *addr)
* \since 1.8
*
* \brief
+ * Splits a string into its host and port components
+ *
+ * \param str[in] The string to parse. May be modified by writing a NUL at the end of
+ * the host part.
+ * \param host[out] Pointer to the host component within \a str.
+ * \param port[out] Pointer to the port component within \a str.
+ * \param flags If set to zero, a port MAY be present. If set to PARSE_PORT_IGNORE, a
+ * port MAY be present but will be ignored. If set to PARSE_PORT_REQUIRE,
+ * a port MUST be present. If set to PARSE_PORT_FORBID, a port MUST NOT
+ * be present.
+ *
+ * \retval 1 Success
+ * \retval 0 Failure
+ */
+int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags);
+
+/*!
+ * \since 1.8
+ *
+ * \brief
* Parse an IPv4 or IPv6 address string.
*
* \details
diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h
index ac09f009b..a0ef3d476 100644
--- a/include/asterisk/pbx.h
+++ b/include/asterisk/pbx.h
@@ -1220,14 +1220,6 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan,
const char *context, const char *exten, int priority,
const char *label, const char *callerid, enum ext_match_t action);
-
-/* every time a write lock is obtained for contexts,
- a counter is incremented. You can check this via the
- following func */
-
-int ast_wrlock_contexts_version(void);
-
-
/*! \brief hashtable functions for contexts */
/*! @{ */
int ast_hashtab_compare_contexts(const void *ah_a, const void *ah_b);
diff --git a/include/asterisk/sched.h b/include/asterisk/sched.h
index 3904daf36..4f5fb4217 100644
--- a/include/asterisk/sched.h
+++ b/include/asterisk/sched.h
@@ -53,7 +53,7 @@ extern "C" {
int _count = 0; \
int _sched_res = -1; \
while (id > -1 && (_sched_res = ast_sched_del(sched, id)) && ++_count < 10) \
- usleep(1000); \
+ usleep(1); \
if (_count == 10 && option_debug > 2) { \
ast_log(LOG_DEBUG, "Unable to cancel schedule ID %d.\n", id); \
} \
@@ -70,7 +70,7 @@ extern "C" {
do { \
int _count = 0; \
while (id > -1 && ast_sched_del(sched, id) && ++_count < 10) { \
- usleep(1000); \
+ usleep(1); \
} \
if (_count == 10) \
ast_log(LOG_WARNING, "Unable to cancel schedule ID %d. This is probably a bug (%s: %s, line %d).\n", id, __FILE__, __PRETTY_FUNCTION__, __LINE__); \
@@ -89,7 +89,7 @@ extern "C" {
int _sched_res = -1; \
while (id > -1 && (_sched_res = ast_sched_del(sched, id)) && ++_count < 10) { \
ast_mutex_unlock(lock); \
- usleep(1000); \
+ usleep(1); \
ast_mutex_lock(lock); \
} \
if (_count == 10 && option_debug > 2) { \
@@ -103,7 +103,7 @@ extern "C" {
do { \
int _count = 0; \
while (id > -1 && ast_sched_del(sched, id) && ++_count < 10) { \
- usleep(1000); \
+ usleep(1); \
} \
if (_count == 10) \
ast_log(LOG_WARNING, "Unable to cancel schedule ID %d. This is probably a bug (%s: %s, line %d).\n", id, __FILE__, __PRETTY_FUNCTION__, __LINE__); \
@@ -122,7 +122,7 @@ extern "C" {
int _count = 0, _res=1; \
void *_data = (void *)ast_sched_find_data(sched, id); \
while (id > -1 && (_res = ast_sched_del(sched, id) && _count++ < 10)) { \
- usleep(1000); \
+ usleep(1); \
} \
if (!_res && _data) \
unrefcall; /* should ref _data! */ \
diff --git a/include/asterisk/translate.h b/include/asterisk/translate.h
index 1d87adbf5..821463bf1 100644
--- a/include/asterisk/translate.h
+++ b/include/asterisk/translate.h
@@ -254,6 +254,14 @@ unsigned int ast_translate_path_steps(format_t dest, format_t src);
*/
format_t ast_translate_available_formats(format_t dest, format_t src);
+/*!
+ * \brief Puts a string representation of the translation path into outbuf
+ * \param translator structure containing the translation path
+ * \param ast_str output buffer
+ * \retval on success pointer to beginning of outbuf. on failure "".
+ */
+const char *ast_translate_path_to_str(struct ast_trans_pvt *t, struct ast_str **str);
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/include/asterisk/unaligned.h b/include/asterisk/unaligned.h
index 63f430f89..9e6e743da 100644
--- a/include/asterisk/unaligned.h
+++ b/include/asterisk/unaligned.h
@@ -50,7 +50,7 @@ static inline unsigned short get_unaligned_uint16(const void *p)
static inline void put_unaligned_uint64(void *p, uint64_t datum)
{
- struct { unsigned int d; } __attribute__((packed,may_alias)) *pp = p;
+ struct { uint64_t d; } __attribute__((packed,may_alias)) *pp = p;
pp->d = datum;
}
diff --git a/main/ccss.c b/main/ccss.c
index 2cf828470..4dcacd360 100644
--- a/main/ccss.c
+++ b/main/ccss.c
@@ -501,38 +501,45 @@ static int count_agents_cb(void *obj, void *arg, void *data, int flags)
return 0;
}
-static const unsigned int CC_OFFER_TIMER_DEFAULT = 20u;
-static const unsigned int CCNR_AVAILABLE_TIMER_DEFAULT = 7200u;
-static const unsigned int CCBS_AVAILABLE_TIMER_DEFAULT = 4800u;
-static const unsigned int CC_RECALL_TIMER_DEFAULT = 20u;
-static const unsigned int CC_MAX_AGENTS_DEFAULT = 5u;
-static const unsigned int CC_MAX_MONITORS_DEFAULT = 5u;
-static const unsigned int GLOBAL_CC_MAX_REQUESTS_DEFAULT = 20u;
+#define CC_OFFER_TIMER_DEFAULT 20 /* Seconds */
+#define CCNR_AVAILABLE_TIMER_DEFAULT 7200 /* Seconds */
+#define CCBS_AVAILABLE_TIMER_DEFAULT 4800 /* Seconds */
+#define CC_RECALL_TIMER_DEFAULT 20 /* Seconds */
+#define CC_MAX_AGENTS_DEFAULT 5
+#define CC_MAX_MONITORS_DEFAULT 5
+#define GLOBAL_CC_MAX_REQUESTS_DEFAULT 20
+
+static const struct ast_cc_config_params cc_default_params = {
+ .cc_agent_policy = AST_CC_AGENT_NEVER,
+ .cc_monitor_policy = AST_CC_MONITOR_NEVER,
+ .cc_offer_timer = CC_OFFER_TIMER_DEFAULT,
+ .ccnr_available_timer = CCNR_AVAILABLE_TIMER_DEFAULT,
+ .ccbs_available_timer = CCBS_AVAILABLE_TIMER_DEFAULT,
+ .cc_recall_timer = CC_RECALL_TIMER_DEFAULT,
+ .cc_max_agents = CC_MAX_AGENTS_DEFAULT,
+ .cc_max_monitors = CC_MAX_MONITORS_DEFAULT,
+ .cc_callback_macro = "",
+ .cc_agent_dialstring = "",
+};
+
+void ast_cc_default_config_params(struct ast_cc_config_params *params)
+{
+ *params = cc_default_params;
+}
struct ast_cc_config_params *__ast_cc_config_params_init(const char *file, int line, const char *function)
{
#if defined(__AST_DEBUG_MALLOC)
- struct ast_cc_config_params *params = __ast_calloc(1, sizeof(*params), file, line, function);
+ struct ast_cc_config_params *params = __ast_malloc(sizeof(*params), file, line, function);
#else
- struct ast_cc_config_params *params = ast_calloc(1, sizeof(*params));
+ struct ast_cc_config_params *params = ast_malloc(sizeof(*params));
#endif
if (!params) {
return NULL;
}
- /* Yeah, I could use the get/set functions, but what's the point since
- * I have direct access to the structure fields in this file.
- */
- params->cc_agent_policy = AST_CC_AGENT_NEVER;
- params->cc_monitor_policy = AST_CC_MONITOR_NEVER;
- params->cc_offer_timer = CC_OFFER_TIMER_DEFAULT;
- params->ccnr_available_timer = CCNR_AVAILABLE_TIMER_DEFAULT;
- params->ccbs_available_timer = CCBS_AVAILABLE_TIMER_DEFAULT;
- params->cc_recall_timer = CC_RECALL_TIMER_DEFAULT;
- params->cc_max_agents = CC_MAX_AGENTS_DEFAULT;
- params->cc_max_monitors = CC_MAX_MONITORS_DEFAULT;
- /* No need to set cc_callback_macro since calloc will 0 it out anyway */
+ ast_cc_default_config_params(params);
return params;
}
diff --git a/main/cdr.c b/main/cdr.c
index a6096976d..c9b58238b 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -84,18 +84,26 @@ static struct sched_context *sched;
static int cdr_sched = -1;
static pthread_t cdr_thread = AST_PTHREADT_NULL;
-#define BATCH_SIZE_DEFAULT 100
-#define BATCH_TIME_DEFAULT 300
-#define BATCH_SCHEDULER_ONLY_DEFAULT 0
-#define BATCH_SAFE_SHUTDOWN_DEFAULT 1
+static int enabled;
+static const int ENABLED_DEFAULT = 1;
-static int enabled; /*! Is the CDR subsystem enabled ? */
-static int unanswered;
static int batchmode;
+static const int BATCHMODE_DEFAULT = 0;
+
+static int unanswered;
+static const int UNANSWERED_DEFAULT = 0;
+
static int batchsize;
+static const int BATCH_SIZE_DEFAULT = 100;
+
static int batchtime;
+static const int BATCH_TIME_DEFAULT = 300;
+
static int batchscheduleronly;
+static const int BATCH_SCHEDULER_ONLY_DEFAULT = 0;
+
static int batchsafeshutdown;
+static const int BATCH_SAFE_SHUTDOWN_DEFAULT = 1;
AST_MUTEX_DEFINE_STATIC(cdr_batch_lock);
@@ -1492,22 +1500,27 @@ static int do_reload(int reload)
int res=0;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
- if ((config = ast_config_load2("cdr.conf", "cdr", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
- return 0;
- if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEUNCHANGED || config == CONFIG_STATUS_FILEINVALID) {
+ if ((config = ast_config_load2("cdr.conf", "cdr", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
return 0;
}
ast_mutex_lock(&cdr_batch_lock);
+ was_enabled = enabled;
+ was_batchmode = batchmode;
+
batchsize = BATCH_SIZE_DEFAULT;
batchtime = BATCH_TIME_DEFAULT;
batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
- was_enabled = enabled;
- was_batchmode = batchmode;
- enabled = 1;
- batchmode = 0;
+ enabled = ENABLED_DEFAULT;
+ batchmode = BATCHMODE_DEFAULT;
+ unanswered = UNANSWERED_DEFAULT;
+
+ if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
+ ast_mutex_unlock(&cdr_batch_lock);
+ return 0;
+ }
/* don't run the next scheduled CDR posting while reloading */
AST_SCHED_DEL(sched, cdr_sched);
diff --git a/main/cel.c b/main/cel.c
index 0af890bde..750a62966 100644
--- a/main/cel.c
+++ b/main/cel.c
@@ -349,6 +349,11 @@ const char *ast_cel_get_type_name(enum ast_cel_event_type type)
const char *ast_cel_get_ama_flag_name(enum ast_cel_ama_flag flag)
{
+ if (flag < 0 || flag >= ARRAY_LEN(cel_ama_flags)) {
+ ast_log(LOG_WARNING, "Invalid AMA flag: %d\n", flag);
+ return "Unknown";
+ }
+
return S_OR(cel_ama_flags[flag], "Unknown");
}
diff --git a/main/channel.c b/main/channel.c
index 32d559d0f..c0a37aaa6 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -5863,6 +5863,7 @@ int ast_do_masquerade(struct ast_channel *original)
struct ast_party_redirecting redirecting;
} exchange;
struct ast_channel *clonechan, *chans[2];
+ struct ast_channel *bridged;
struct ast_cdr *cdr;
format_t rformat = original->readformat;
format_t wformat = original->writeformat;
@@ -6185,6 +6186,14 @@ int ast_do_masquerade(struct ast_channel *original)
pthread_kill(original->blocker, SIGURG);
ast_debug(1, "Done Masquerading %s (%d)\n", original->name, original->_state);
+ if ((bridged = ast_bridged_channel(original))) {
+ ast_channel_lock(bridged);
+ ast_indicate(bridged, AST_CONTROL_SRCCHANGE);
+ ast_channel_unlock(bridged);
+ }
+
+ ast_indicate(original, AST_CONTROL_SRCCHANGE);
+
done:
/* it is possible for the clone channel to disappear during this */
if (clonechan) {
@@ -6196,6 +6205,7 @@ done:
ast_channel_unlock(original);
ao2_link(channels, original);
}
+
return 0;
}
@@ -6716,8 +6726,8 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha
manager_bridge_event(1, 1, c0, c1);
/* Before we enter in and bridge these two together tell them both the source of audio has changed */
- ast_indicate(c0, AST_CONTROL_SRCUPDATE);
- ast_indicate(c1, AST_CONTROL_SRCUPDATE);
+ ast_indicate(c0, AST_CONTROL_SRCCHANGE);
+ ast_indicate(c1, AST_CONTROL_SRCCHANGE);
for (/* ever */;;) {
struct timeval now = { 0, };
diff --git a/main/cli.c b/main/cli.c
index c9dbc6d02..77e52b863 100644
--- a/main/cli.c
+++ b/main/cli.c
@@ -46,6 +46,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/lock.h"
#include "editline/readline/readline.h"
#include "asterisk/threadstorage.h"
+#include "asterisk/translate.h"
/*!
* \brief List of restrictions per user.
@@ -302,6 +303,28 @@ static char *handle_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args
return CLI_SUCCESS;
}
+static char *handle_core_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core reload";
+ e->usage =
+ "Usage: core reload\n"
+ " Execute a global reload.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != e->args) {
+ return CLI_SHOWUSAGE;
+ }
+
+ ast_module_reload(NULL);
+
+ return CLI_SUCCESS;
+}
/*!
* \brief Find the debug or verbose file setting
* \arg debug 1 for debug, 0 for verbose
@@ -1343,6 +1366,8 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
struct ast_str *out = ast_str_thread_get(&ast_str_thread_global_buf, 16);
char cdrtime[256];
char nf[256], wf[256], rf[256];
+ struct ast_str *write_transpath = ast_str_alloca(256);
+ struct ast_str *read_transpath = ast_str_alloca(256);
long elapsed_seconds=0;
int hour=0, min=0, sec=0;
#ifdef CHANNEL_TRACE
@@ -1398,8 +1423,8 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
" NativeFormats: %s\n"
" WriteFormat: %s\n"
" ReadFormat: %s\n"
- " WriteTranscode: %s\n"
- " ReadTranscode: %s\n"
+ " WriteTranscode: %s %s\n"
+ " ReadTranscode: %s %s\n"
"1st File Descriptor: %d\n"
" Frames in: %d%s\n"
" Frames out: %d%s\n"
@@ -1426,7 +1451,9 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
ast_getformatname_multiple(wf, sizeof(wf), c->writeformat),
ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
c->writetrans ? "Yes" : "No",
+ ast_translate_path_to_str(c->writetrans, &write_transpath),
c->readtrans ? "Yes" : "No",
+ ast_translate_path_to_str(c->readtrans, &read_transpath),
c->fds[0],
c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
@@ -1485,7 +1512,13 @@ char *ast_complete_channels(const char *line, const char *word, int pos, int sta
return NULL;
}
- if (!(iter = ast_channel_iterator_by_name_new(word, strlen(word)))) {
+ if (ast_strlen_zero(word)) {
+ iter = ast_channel_iterator_all_new();
+ } else {
+ iter = ast_channel_iterator_by_name_new(word, strlen(word));
+ }
+
+ if (!iter) {
return NULL;
}
@@ -1609,7 +1642,9 @@ static struct ast_cli_entry cli_cli[] = {
AST_CLI_DEFINE(handle_load, "Load a module by name"),
- AST_CLI_DEFINE(handle_reload, "Reload configuration"),
+ AST_CLI_DEFINE(handle_reload, "Reload configuration for a module"),
+
+ AST_CLI_DEFINE(handle_core_reload, "Global reload"),
AST_CLI_DEFINE(handle_unload, "Unload a module by name"),
diff --git a/main/netsock2.c b/main/netsock2.c
index 929f4b337..99f7eac89 100644
--- a/main/netsock2.c
+++ b/main/netsock2.c
@@ -118,7 +118,7 @@ char *ast_sockaddr_stringify_fmt(const struct ast_sockaddr *sa, int format)
return ast_str_buffer(str);
}
-int static _ast_sockaddr_parse(char *str, char **host, char **port, int flags)
+int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags)
{
char *s = str;
@@ -187,7 +187,7 @@ int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
int e;
s = ast_strdupa(str);
- if (!_ast_sockaddr_parse(s, &host, &port, flags)) {
+ if (!ast_sockaddr_split_hostport(s, &host, &port, flags)) {
return 0;
}
@@ -233,7 +233,7 @@ int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str,
int e, i, res_cnt;
s = ast_strdupa(str);
- if (!_ast_sockaddr_parse(s, &host, &port, flags)) {
+ if (!ast_sockaddr_split_hostport(s, &host, &port, flags)) {
return 0;
}
@@ -343,7 +343,9 @@ uint16_t _ast_sockaddr_port(const struct ast_sockaddr *addr, const char *file, i
addr->len == sizeof(struct sockaddr_in6)) {
return ntohs(((struct sockaddr_in6 *)&addr->ss)->sin6_port);
}
- ast_log(__LOG_DEBUG, file, line, func, "Not an IPv4 nor IPv6 address, cannot get port.\n");
+ if (option_debug >= 1) {
+ ast_log(__LOG_DEBUG, file, line, func, "Not an IPv4 nor IPv6 address, cannot get port.\n");
+ }
return 0;
}
@@ -355,7 +357,7 @@ void _ast_sockaddr_set_port(struct ast_sockaddr *addr, uint16_t port, const char
} else if (addr->ss.ss_family == AF_INET6 &&
addr->len == sizeof(struct sockaddr_in6)) {
((struct sockaddr_in6 *)&addr->ss)->sin6_port = htons(port);
- } else {
+ } else if (option_debug >= 1) {
ast_log(__LOG_DEBUG, file, line, func,
"Not an IPv4 nor IPv6 address, cannot set port.\n");
}
@@ -485,7 +487,7 @@ int _ast_sockaddr_to_sin(const struct ast_sockaddr *addr,
return 0;
}
- if (addr->ss.ss_family != AF_INET) {
+ if (addr->ss.ss_family != AF_INET && option_debug >= 1) {
ast_log(__LOG_DEBUG, file, line, func, "Address family is not AF_INET\n");
}
@@ -498,7 +500,7 @@ void _ast_sockaddr_from_sin(struct ast_sockaddr *addr, const struct sockaddr_in
{
memcpy(&addr->ss, sin, sizeof(*sin));
- if (addr->ss.ss_family != AF_INET) {
+ if (addr->ss.ss_family != AF_INET && option_debug >= 1) {
ast_log(__LOG_DEBUG, file, line, func, "Address family is not AF_INET\n");
}
diff --git a/main/pbx.c b/main/pbx.c
index 4216b853a..67163de3e 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -1164,7 +1164,11 @@ static struct pbx_builtin {
static struct ast_context *contexts;
static struct ast_hashtab *contexts_table = NULL;
-AST_RWLOCK_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */
+/*!\brief Lock for the ast_context list
+ * This lock MUST be recursive, or a deadlock on reload may result. See
+ * https://issues.asterisk.org/view.php?id=17643
+ */
+AST_MUTEX_DEFINE_STATIC(conlock);
static AST_RWLIST_HEAD_STATIC(apps, ast_app);
@@ -6850,7 +6854,7 @@ struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts,
ast_hashtab_insert_safe(contexts_table, tmp); /*put this context into the tree */
ast_unlock_contexts();
ast_debug(1, "Registered context '%s'(%p) in table %p registrar: %s\n", tmp->name, tmp, contexts_table, registrar);
- ast_verb(3, "Registered extension context '%s' (%p) in table %p; registrar: %s\n", tmp->name, tmp, contexts_table, registrar);
+ ast_verb(3, "Registered extension context '%s'; registrar: %s\n", tmp->name, registrar);
} else {
tmp->next = *local_contexts;
if (exttable)
@@ -6858,7 +6862,7 @@ struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts,
*local_contexts = tmp;
ast_debug(1, "Registered context '%s'(%p) in local table %p; registrar: %s\n", tmp->name, tmp, exttable, registrar);
- ast_verb(3, "Registered extension context '%s' (%p) in local table %p; registrar: %s\n", tmp->name, tmp, exttable, registrar);
+ ast_verb(3, "Registered extension context '%s'; registrar: %s\n", tmp->name, registrar);
}
return tmp;
}
@@ -7016,7 +7020,6 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
*/
struct timeval begintime, writelocktime, endlocktime, enddeltime;
- int wrlock_ver;
begintime = ast_tvnow();
ast_rdlock_contexts();
@@ -7025,15 +7028,6 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
context_merge(extcontexts, exttable, tmp, registrar);
}
ast_hashtab_end_traversal(iter);
- wrlock_ver = ast_wrlock_contexts_version();
-
- ast_unlock_contexts(); /* this feels real retarded, but you must do
- what you must do If this isn't done, the following
- wrlock is a guraranteed deadlock */
- ast_wrlock_contexts();
- if (ast_wrlock_contexts_version() > wrlock_ver+1) {
- ast_log(LOG_WARNING,"==================!!!!!!!!!!!!!!!Something changed the contexts in the middle of merging contexts!\n");
- }
AST_RWLIST_WRLOCK(&hints);
writelocktime = ast_tvnow();
@@ -8209,11 +8203,11 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
}
if (tmp->matchcid) {
- ast_verb(3, "Added extension '%s' priority %d (CID match '%s') to %s (%p)\n",
- tmp->exten, tmp->priority, tmp->cidmatch, con->name, con);
+ ast_verb(3, "Added extension '%s' priority %d (CID match '%s') to %s\n",
+ tmp->exten, tmp->priority, tmp->cidmatch, con->name);
} else {
- ast_verb(3, "Added extension '%s' priority %d to %s (%p)\n",
- tmp->exten, tmp->priority, con->name, con);
+ ast_verb(3, "Added extension '%s' priority %d to %s\n",
+ tmp->exten, tmp->priority, con->name);
}
return 0;
@@ -9836,32 +9830,23 @@ int load_pbx(void)
return 0;
}
-static int conlock_wrlock_version = 0;
-
-int ast_wrlock_contexts_version(void)
-{
- return conlock_wrlock_version;
-}
/*
* Lock context list functions ...
*/
int ast_wrlock_contexts()
{
- int res = ast_rwlock_wrlock(&conlock);
- if (!res)
- ast_atomic_fetchadd_int(&conlock_wrlock_version, 1);
- return res;
+ return ast_mutex_lock(&conlock);
}
int ast_rdlock_contexts()
{
- return ast_rwlock_rdlock(&conlock);
+ return ast_mutex_lock(&conlock);
}
int ast_unlock_contexts()
{
- return ast_rwlock_unlock(&conlock);
+ return ast_mutex_unlock(&conlock);
}
/*
diff --git a/main/sched.c b/main/sched.c
index 78de1a99e..8a3602a39 100644
--- a/main/sched.c
+++ b/main/sched.c
@@ -604,13 +604,13 @@ int ast_sched_runq(struct sched_context *con)
ast_mutex_lock(&con->lock);
+ when = ast_tvadd(ast_tvnow(), ast_tv(0, 1000));
for (numevents = 0; (current = ast_heap_peek(con->sched_heap, 1)); numevents++) {
/* schedule all events which are going to expire within 1ms.
* We only care about millisecond accuracy anyway, so this will
* help us get more than one event at one time if they are very
* close together.
*/
- when = ast_tvadd(ast_tvnow(), ast_tv(0, 1000));
if (ast_tvcmp(current->when, when) != -1) {
break;
}
diff --git a/main/translate.c b/main/translate.c
index 24d886473..61b4a4686 100644
--- a/main/translate.c
+++ b/main/translate.c
@@ -45,10 +45,23 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
/*! \brief the list of translators */
static AST_RWLIST_HEAD_STATIC(translators, ast_translator);
+
+/*! \brief these values indicate how a translation path will affect the sample rate
+ *
+ * \note These must stay in this order. They are ordered by most optimal selection first.
+ */
+enum path_samp_change {
+ RATE_CHANGE_NONE = 0, /*!< path uses the same sample rate consistently */
+ RATE_CHANGE_UPSAMP = 1, /*!< path will up the sample rate during a translation */
+ RATE_CHANGE_DOWNSAMP = 2, /*!< path will have to down the sample rate during a translation. */
+ RATE_CHANGE_UPSAMP_DOWNSAMP = 3, /*!< path will both up and down the sample rate during translation */
+};
+
struct translator_path {
struct ast_translator *step; /*!< Next step translator */
unsigned int cost; /*!< Complete cost to destination */
unsigned int multistep; /*!< Multiple conversions required for this translation */
+ enum path_samp_change rate_change; /*!< does this path require a sample rate change, if so what kind. */
};
/*! \brief a matrix that, for any pair of supported formats,
@@ -402,6 +415,24 @@ static void calc_cost(struct ast_translator *t, int seconds)
t->cost = 1;
}
+static enum path_samp_change get_rate_change_result(format_t src, format_t dst)
+{
+ int src_rate = ast_format_rate(src);
+ int dst_rate = ast_format_rate(dst);
+
+ /* if src rate is less than dst rate, a sample upgrade is required */
+ if (src_rate < dst_rate) {
+ return RATE_CHANGE_UPSAMP;
+ }
+
+ /* if src rate is larger than dst rate, a downgrade is required */
+ if (src_rate > dst_rate) {
+ return RATE_CHANGE_DOWNSAMP;
+ }
+
+ return RATE_CHANGE_NONE;
+}
+
/*!
* \brief rebuild a translation matrix.
* \note This function expects the list of translators to be locked
@@ -409,6 +440,8 @@ static void calc_cost(struct ast_translator *t, int seconds)
static void rebuild_matrix(int samples)
{
struct ast_translator *t;
+ int new_rate_change;
+ int newcost;
int x; /* source format index */
int y; /* intermediate format index */
int z; /* destination format index */
@@ -427,10 +460,21 @@ static void rebuild_matrix(int samples)
if (samples)
calc_cost(t, samples);
-
- if (!tr_matrix[x][z].step || t->cost < tr_matrix[x][z].cost) {
+
+ new_rate_change = get_rate_change_result(1LL << t->srcfmt, 1LL << t->dstfmt);
+
+ /* this translator is the best choice if any of the below are true.
+ * 1. no translation path is set between x and z yet.
+ * 2. the new translation costs less and sample rate is no worse than old one.
+ * 3. the new translation has a better sample rate conversion than the old one.
+ */
+ if (!tr_matrix[x][z].step ||
+ ((t->cost < tr_matrix[x][z].cost) && (new_rate_change <= tr_matrix[x][z].rate_change)) ||
+ (new_rate_change < tr_matrix[x][z].rate_change)) {
+
tr_matrix[x][z].step = t;
tr_matrix[x][z].cost = t->cost;
+ tr_matrix[x][z].rate_change = new_rate_change;
}
}
@@ -442,31 +486,73 @@ static void rebuild_matrix(int samples)
*/
for (;;) {
int changed = 0;
+ int better_choice = 0;
for (x = 0; x < MAX_FORMAT; x++) { /* source format */
for (y = 0; y < MAX_FORMAT; y++) { /* intermediate format */
if (x == y) /* skip ourselves */
continue;
-
- for (z = 0; z<MAX_FORMAT; z++) { /* dst format */
- int newcost;
-
+ for (z = 0; z < MAX_FORMAT; z++) { /* dst format */
if (z == x || z == y) /* skip null conversions */
continue;
if (!tr_matrix[x][y].step) /* no path from x to y */
continue;
if (!tr_matrix[y][z].step) /* no path from y to z */
continue;
+
+ /* Does x->y->z result in a less optimal sample rate change?
+ * Never downgrade the sample rate conversion quality regardless
+ * of any cost improvements */
+ if (tr_matrix[x][z].step &&
+ ((tr_matrix[x][z].rate_change < tr_matrix[x][y].rate_change) ||
+ (tr_matrix[x][z].rate_change < tr_matrix[y][z].rate_change))) {
+ continue;
+ }
+
+ /* is x->y->z a better sample rate confersion that the current x->z? */
+ new_rate_change = tr_matrix[x][y].rate_change + tr_matrix[y][z].rate_change;
+
+ /* calculate cost from x->y->z */
newcost = tr_matrix[x][y].cost + tr_matrix[y][z].cost;
- if (tr_matrix[x][z].step && newcost >= tr_matrix[x][z].cost)
- continue; /* x->y->z is more expensive than
- * the existing path */
+
+ /* Is x->y->z a better choice than x->z?
+ * There are three conditions for x->y->z to be a better choice than x->z
+ * 1. if there is no step directly between x->z then x->y->z is the best and only current option.
+ * 2. if x->y->z costs less and the sample rate conversion is no less optimal.
+ * 3. if x->y->z results in a more optimal sample rate conversion. */
+ if (!tr_matrix[x][z].step) {
+ better_choice = 1;
+ } else if ((newcost < tr_matrix[x][z].cost) && (new_rate_change <= tr_matrix[x][z].rate_change)) {
+ better_choice = 1;
+ } else if (new_rate_change < tr_matrix[x][z].rate_change) {
+ better_choice = 1;
+ } else {
+ better_choice = 0;
+ }
+
+ if (!better_choice) {
+ continue;
+ }
/* ok, we can get from x to z via y with a cost that
- is the sum of the transition from x to y and
- from y to z */
-
+ is the sum of the transition from x to y and from y to z */
tr_matrix[x][z].step = tr_matrix[x][y].step;
tr_matrix[x][z].cost = newcost;
tr_matrix[x][z].multistep = 1;
+
+ /* now calculate what kind of sample rate change is required for this multi-step path
+ *
+ * if both paths require a change in rate, and they are not in the same direction
+ * then this is a up sample down sample conversion scenario. */
+ if ((tr_matrix[x][y].rate_change > RATE_CHANGE_NONE) &&
+ (tr_matrix[y][z].rate_change > RATE_CHANGE_NONE) &&
+ (tr_matrix[x][y].rate_change != tr_matrix[y][z].rate_change)) {
+
+ tr_matrix[x][z].rate_change = RATE_CHANGE_UPSAMP_DOWNSAMP;
+ } else {
+ /* else just set the rate change to whichever is worse */
+ tr_matrix[x][z].rate_change = tr_matrix[x][y].rate_change > tr_matrix[y][z].rate_change
+ ? tr_matrix[x][y].rate_change : tr_matrix[y][z].rate_change;
+ }
+
ast_debug(3, "Discovered %d cost path from %s to %s, via %s\n", tr_matrix[x][z].cost,
ast_getformatname(1LL << x), ast_getformatname(1LL << z), ast_getformatname(1LL << y));
changed++;
@@ -478,30 +564,134 @@ static void rebuild_matrix(int samples)
}
}
+const char *ast_translate_path_to_str(struct ast_trans_pvt *p, struct ast_str **str)
+{
+ struct ast_trans_pvt *pn = p;
+
+ if (!p || !p->t) {
+ return "";
+ }
+
+ ast_str_set(str, 0, "%s", ast_getformatname(1LL << p->t->srcfmt));
+
+ while ( (p = pn) ) {
+ pn = p->next;
+ ast_str_append(str, 0, "->%s", ast_getformatname(1LL << p->t->dstfmt));
+ }
+
+ return ast_str_buffer(*str);
+}
+
+static char *complete_trans_path_choice(const char *line, const char *word, int pos, int state)
+{
+ int which = 0;
+ int wordlen = strlen(word);
+ int i;
+ char *ret = NULL;
+ size_t len = 0;
+ const struct ast_format_list *format_list = ast_get_format_list(&len);
+
+ for (i = 0; i < len; i++) {
+ if (!(format_list[i].bits & AST_FORMAT_AUDIO_MASK)) {
+ continue;
+ }
+ if (!strncasecmp(word, format_list[i].name, wordlen) && ++which > state) {
+ ret = ast_strdup(format_list[i].name);
+ break;
+ }
+ }
+ return ret;
+}
+
static char *handle_cli_core_show_translation(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
-#define SHOW_TRANS 16
+#define SHOW_TRANS 64
+ static const char * const option1[] = { "recalc", "paths", NULL };
int x, y, z;
int curlen = 0, longest = 0, magnitude[SHOW_TRANS] = { 0, };
switch (cmd) {
case CLI_INIT:
- e->command = "core show translation [recalc]";
+ e->command = "core show translation";
e->usage =
- "Usage: core show translation [recalc [<recalc seconds>]]\n"
- " Displays known codec translators and the cost associated\n"
- " with each conversion. If the argument 'recalc' is supplied along\n"
- " with optional number of seconds to test a new test will be performed\n"
- " as the chart is being displayed.\n";
+ "Usage: 'core show translation' can be used in two ways.\n"
+ " 1. 'core show translation [recalc [<recalc seconds>]]\n"
+ " Displays known codec translators and the cost associated\n"
+ " with each conversion. If the argument 'recalc' is supplied along\n"
+ " with optional number of seconds to test a new test will be performed\n"
+ " as the chart is being displayed.\n"
+ " 2. 'core show translation paths [codec]'\n"
+ " This will display all the translation paths associated with a codec\n";
return NULL;
case CLI_GENERATE:
+ if (a->pos == 3) {
+ return ast_cli_complete(a->word, option1, a->n);
+ }
+ if (a->pos == 4 && !strcasecmp(a->argv[3], option1[1])) {
+ return complete_trans_path_choice(a->line, a->word, a->pos, a->n);
+ }
return NULL;
}
if (a->argc > 5)
return CLI_SHOWUSAGE;
- if (a->argv[3] && !strcasecmp(a->argv[3], "recalc")) {
+ if (a->argv[3] && !strcasecmp(a->argv[3], option1[1]) && a->argc == 5) {
+ format_t input_src = 0;
+ format_t src = 0;
+ size_t len = 0;
+ int dst;
+ int i;
+ const struct ast_format_list *format_list = ast_get_format_list(&len);
+ struct ast_str *str = ast_str_alloca(256);
+ struct ast_translator *step;
+
+ for (i = 0; i < len; i++) {
+ if (!(format_list[i].bits & AST_FORMAT_AUDIO_MASK)) {
+ continue;
+ }
+ if (!strncasecmp(format_list[i].name, a->argv[4], strlen(format_list[i].name))) {
+ input_src = format_list[i].bits;
+ }
+ }
+
+ if (!input_src) {
+ ast_cli(a->fd, "Source codec \"%s\" is not found.\n", a->argv[4]);
+ return CLI_FAILURE;
+ }
+
+ AST_RWLIST_RDLOCK(&translators);
+ ast_cli(a->fd, "--- Translation paths SRC Codec \"%s\" sample rate %d ---\n", a->argv[4], ast_format_rate(input_src));
+ for (i = 0; i < len; i++) {
+ if (!(format_list[i].bits & AST_FORMAT_AUDIO_MASK) || (format_list[i].bits == input_src)) {
+ continue;
+ }
+ dst = powerof(format_list[i].bits);
+ src = powerof(input_src);
+ ast_str_reset(str);
+ if (tr_matrix[src][dst].step) {
+ ast_str_append(&str, 0, "%s", ast_getformatname(1LL << tr_matrix[src][dst].step->srcfmt));
+ while (src != dst) {
+ step = tr_matrix[src][dst].step;
+ if (!step) {
+ ast_str_reset(str);
+ break;
+ }
+ ast_str_append(&str, 0, "->%s", ast_getformatname(1LL << step->dstfmt));
+ src = step->dstfmt;
+ }
+ }
+
+ if (ast_strlen_zero(ast_str_buffer(str))) {
+ ast_str_set(&str, 0, "No Translation Path");
+ }
+
+ ast_cli(a->fd, "\t%-10.10s To %-10.10s: %-60.60s\n", a->argv[4], format_list[i].name, ast_str_buffer(str));
+ }
+ AST_RWLIST_UNLOCK(&translators);
+
+ return CLI_SUCCESS;
+ } else if (a->argv[3] && !strcasecmp(a->argv[3], "recalc")) {
z = a->argv[4] ? atoi(a->argv[4]) : 1;
if (z <= 0) {
@@ -526,22 +716,33 @@ static char *handle_cli_core_show_translation(struct ast_cli_entry *e, int cmd,
ast_cli(a->fd, " Source Format (Rows) Destination Format (Columns)\n\n");
/* Get the length of the longest (usable?) codec name, so we know how wide the left side should be */
for (x = 0; x < SHOW_TRANS; x++) {
+ /* translation only applies to audio right now. */
+ if (!(AST_FORMAT_AUDIO_MASK & (1LL << (x))))
+ continue;
curlen = strlen(ast_getformatname(1LL << (x)));
if (curlen > longest)
longest = curlen;
for (y = 0; y < SHOW_TRANS; y++) {
+ if (!(AST_FORMAT_AUDIO_MASK & (1LL << (y))))
+ continue;
if (tr_matrix[x][y].cost > pow(10, magnitude[x])) {
magnitude[y] = floor(log10(tr_matrix[x][y].cost));
}
}
}
for (x = -1; x < SHOW_TRANS; x++) {
- struct ast_str *out = ast_str_alloca(125);
+ struct ast_str *out = ast_str_alloca(256);
+ /* translation only applies to audio right now. */
+ if (x >= 0 && !(AST_FORMAT_AUDIO_MASK & (1LL << (x))))
+ continue;
/*Go ahead and move to next iteration if dealing with an unknown codec*/
if(x >= 0 && !strcmp(ast_getformatname(1LL << (x)), "unknown"))
continue;
ast_str_set(&out, -1, " ");
for (y = -1; y < SHOW_TRANS; y++) {
+ /* translation only applies to audio right now. */
+ if (y >= 0 && !(AST_FORMAT_AUDIO_MASK & (1LL << (y))))
+ continue;
/*Go ahead and move to next iteration if dealing with an unknown codec*/
if (y >= 0 && !strcmp(ast_getformatname(1LL << (y)), "unknown"))
continue;
@@ -715,37 +916,65 @@ void ast_translator_deactivate(struct ast_translator *t)
format_t ast_translator_best_choice(format_t *dst, format_t *srcs)
{
int x,y;
+ int better = 0;
+ int besttime = INT_MAX;
+ int beststeps = INT_MAX;
+ unsigned int best_rate_change = INT_MAX;
format_t best = -1;
format_t bestdst = 0;
format_t cur, cursrc;
- int besttime = INT_MAX;
- int beststeps = INT_MAX;
format_t common = ((*dst) & (*srcs)) & AST_FORMAT_AUDIO_MASK; /* are there common formats ? */
if (common) { /* yes, pick one and return */
for (cur = 1, y = 0; y <= MAX_AUDIO_FORMAT; cur <<= 1, y++) {
- if (cur & common) /* guaranteed to find one */
- break;
+ if (!(cur & common)) {
+ continue;
+ }
+
+ /* We are guaranteed to find one common format. */
+ if (best == -1) {
+ best = cur;
+ continue;
+ }
+ /* If there are multiple common formats, pick the one with the highest sample rate */
+ if (ast_format_rate(best) < ast_format_rate(cur)) {
+ best = cur;
+ continue;
+ }
}
/* We are done, this is a common format to both. */
- *srcs = *dst = cur;
+ *srcs = *dst = best;
return 0;
- } else { /* No, we will need to translate */
+ } else { /* No, we will need to translate */
AST_RWLIST_RDLOCK(&translators);
for (cur = 1, y = 0; y <= MAX_AUDIO_FORMAT; cur <<= 1, y++) {
- if (! (cur & *dst))
+ if (! (cur & *dst)) {
continue;
+ }
for (cursrc = 1, x = 0; x <= MAX_AUDIO_FORMAT; cursrc <<= 1, x++) {
- if (!(*srcs & cursrc) || !tr_matrix[x][y].step ||
- tr_matrix[x][y].cost > besttime)
- continue; /* not existing or no better */
- if (tr_matrix[x][y].cost < besttime ||
- tr_matrix[x][y].multistep < beststeps) {
+ if (!(*srcs & cursrc) || !tr_matrix[x][y].step) {
+ continue;
+ }
+
+ /* This is a better choice if any of the following are true.
+ * 1. The sample rate conversion is better than the current pick.
+ * 2. the sample rate conversion is no worse than the current pick and the cost or multistep is better
+ */
+ better = 0;
+ if (tr_matrix[x][y].rate_change < best_rate_change) {
+ better = 1; /* this match has a better rate conversion */
+ }
+ if ((tr_matrix[x][y].rate_change <= best_rate_change) &&
+ (tr_matrix[x][y].cost < besttime || tr_matrix[x][y].multistep < beststeps)) {
+ better = 1; /* this match has no worse rate conversion and the conversion cost is less */
+ }
+ if (better) {
/* better than what we have so far */
best = cursrc;
bestdst = cur;
besttime = tr_matrix[x][y].cost;
beststeps = tr_matrix[x][y].multistep;
+ best_rate_change = tr_matrix[x][y].rate_change;
}
}
}
diff --git a/main/utils.c b/main/utils.c
index 5961a7339..6f2c884d0 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -1615,7 +1615,8 @@ ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr
size_t space = (*pool_head)->size - (*pool_head)->used;
size_t to_alloc = needed + sizeof(ast_string_field_allocation);
- if (__builtin_expect(to_alloc > space, 0)) {
+ /* This +1 accounts for alignment on SPARC */
+ if (__builtin_expect(to_alloc + 1 > space, 0)) {
size_t new_size = (*pool_head)->size;
while (new_size < to_alloc) {
@@ -1632,6 +1633,13 @@ ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr
}
result = (*pool_head)->base + (*pool_head)->used;
+#ifdef __sparc__
+ /* SPARC requires that the allocation field be aligned. */
+ if ((long) result % sizeof(ast_string_field_allocation)) {
+ result++;
+ (*pool_head)->used++;
+ }
+#endif
(*pool_head)->used += to_alloc;
(*pool_head)->active += needed;
result += sizeof(ast_string_field_allocation);
@@ -1706,6 +1714,12 @@ void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
}
} else {
target = (*pool_head)->base + (*pool_head)->used + sizeof(ast_string_field_allocation);
+#ifdef __sparc__
+ if ((long) target % sizeof(ast_string_field_allocation)) {
+ target++;
+ space--;
+ }
+#endif
available = space - sizeof(ast_string_field_allocation);
}
diff --git a/pbx/pbx_config.c b/pbx/pbx_config.c
index ed1a2cd8c..1617d5f86 100644
--- a/pbx/pbx_config.c
+++ b/pbx/pbx_config.c
@@ -1737,13 +1737,13 @@ static int pbx_load_module(void)
static int load_module(void)
{
- if (pbx_load_module())
- return AST_MODULE_LOAD_DECLINE;
-
if (static_config && !write_protect_config)
ast_cli_register(&cli_dialplan_save);
ast_cli_register_multiple(cli_pbx_config, ARRAY_LEN(cli_pbx_config));
+ if (pbx_load_module())
+ return AST_MODULE_LOAD_DECLINE;
+
return AST_MODULE_LOAD_SUCCESS;
}
diff --git a/res/res_fax.c b/res/res_fax.c
index 03fd128a7..3da2d02f8 100644
--- a/res/res_fax.c
+++ b/res/res_fax.c
@@ -387,9 +387,6 @@ static struct ast_fax_session_details *session_details_new(void)
d->modems = general_options.modems;
d->minrate = general_options.minrate;
d->maxrate = general_options.maxrate;
- ast_string_field_set(d, result, "FAILED");
- ast_string_field_set(d, resultstr, "error starting fax session");
- ast_string_field_set(d, error, "INIT_ERROR");
return d;
}
@@ -500,6 +497,45 @@ static int ast_fax_modem_to_str(enum ast_fax_modems bits, char *tbuf, size_t buf
return 0;
}
+static int check_modem_rate(enum ast_fax_modems modems, unsigned int rate)
+{
+ switch (rate) {
+ case 2400:
+ if (!(modems & (AST_FAX_MODEM_V27 | AST_FAX_MODEM_V34))) {
+ return 1;
+ }
+ break;
+ case 4800:
+ if (!(modems & (AST_FAX_MODEM_V27 | AST_FAX_MODEM_V34))) {
+ return 1;
+ }
+ break;
+ case 7200:
+ case 9600:
+ if (!(modems & (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V29 | AST_FAX_MODEM_V34))) {
+ return 1;
+ }
+ break;
+ case 12000:
+ case 14400:
+ if (!(modems & (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V34))) {
+ return 1;
+ }
+ break;
+ case 28800:
+ case 33600:
+ if (!(modems & AST_FAX_MODEM_V34)) {
+ return 1;
+ }
+ break;
+ default:
+ /* this should never happen */
+ return 1;
+ }
+
+ return 0;
+}
+
/*! \brief register a FAX technology module */
int ast_fax_tech_register(struct ast_fax_tech *tech)
{
@@ -1363,7 +1399,7 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
/*! \brief initiate a receive FAX session */
static int receivefax_exec(struct ast_channel *chan, const char *data)
{
- char *parse;
+ char *parse, modems[128] = "";
int channel_alive;
struct ast_fax_session_details *details;
struct ast_fax_document *doc;
@@ -1390,8 +1426,40 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
return -1;
}
+ ast_string_field_set(details, result, "FAILED");
+ ast_string_field_set(details, resultstr, "error starting fax session");
+ ast_string_field_set(details, error, "INIT_ERROR");
set_channel_variables(chan, details);
+ if (details->maxrate < details->minrate) {
+ ast_string_field_set(details, error, "INVALID_ARGUMENTS");
+ ast_string_field_set(details, resultstr, "maxrate is less than minrate");
+ set_channel_variables(chan, details);
+ ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", details->maxrate, details->minrate);
+ ao2_ref(details, -1);
+ return -1;
+ }
+
+ if (check_modem_rate(details->modems, details->minrate)) {
+ ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
+ ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %d\n", modems, details->minrate);
+ ast_string_field_set(details, error, "INVALID_ARGUMENTS");
+ ast_string_field_set(details, resultstr, "incompatible 'modems' and 'minrate' settings");
+ set_channel_variables(chan, details);
+ ao2_ref(details, -1);
+ return -1;
+ }
+
+ if (check_modem_rate(details->modems, details->maxrate)) {
+ ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
+ ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %d\n", modems, details->maxrate);
+ ast_string_field_set(details, error, "INVALID_ARGUMENTS");
+ ast_string_field_set(details, resultstr, "incompatible 'modems' and 'maxrate' settings");
+ set_channel_variables(chan, details);
+ ao2_ref(details, -1);
+ return -1;
+ }
+
if (ast_strlen_zero(data)) {
ast_string_field_set(details, error, "INVALID_ARGUMENTS");
ast_string_field_set(details, resultstr, "invalid arguments");
@@ -1768,7 +1836,7 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
/*! \brief initiate a send FAX session */
static int sendfax_exec(struct ast_channel *chan, const char *data)
{
- char *parse, *filenames, *c;
+ char *parse, *filenames, *c, modems[128] = "";
int channel_alive, file_count;
struct ast_fax_session_details *details;
struct ast_fax_document *doc;
@@ -1795,8 +1863,40 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
return -1;
}
+ ast_string_field_set(details, result, "FAILED");
+ ast_string_field_set(details, resultstr, "error starting fax session");
+ ast_string_field_set(details, error, "INIT_ERROR");
set_channel_variables(chan, details);
+ if (details->maxrate < details->minrate) {
+ ast_string_field_set(details, error, "INVALID_ARGUMENTS");
+ ast_string_field_set(details, resultstr, "maxrate is less than minrate");
+ set_channel_variables(chan, details);
+ ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", details->maxrate, details->minrate);
+ ao2_ref(details, -1);
+ return -1;
+ }
+
+ if (check_modem_rate(details->modems, details->minrate)) {
+ ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
+ ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %d\n", modems, details->minrate);
+ ast_string_field_set(details, error, "INVALID_ARGUMENTS");
+ ast_string_field_set(details, resultstr, "incompatible 'modems' and 'minrate' settings");
+ set_channel_variables(chan, details);
+ ao2_ref(details, -1);
+ return -1;
+ }
+
+ if (check_modem_rate(details->modems, details->maxrate)) {
+ ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
+ ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %d\n", modems, details->maxrate);
+ ast_string_field_set(details, error, "INVALID_ARGUMENTS");
+ ast_string_field_set(details, resultstr, "incompatible 'modems' and 'maxrate' settings");
+ set_channel_variables(chan, details);
+ ao2_ref(details, -1);
+ return -1;
+ }
+
if (ast_strlen_zero(data)) {
ast_string_field_set(details, error, "INVALID_ARGUMENTS");
ast_string_field_set(details, resultstr, "invalid arguments");
@@ -2292,6 +2392,7 @@ static int set_config(const char *config_file)
struct ast_config *cfg;
struct ast_variable *v;
struct ast_flags config_flags = { 0 };
+ char modems[128] = "";
/* set defaults */
general_options.minrate = RES_FAX_MINRATE;
@@ -2342,6 +2443,23 @@ static int set_config(const char *config_file)
ast_config_destroy(cfg);
+ if (general_options.maxrate < general_options.minrate) {
+ ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", general_options.maxrate, general_options.minrate);
+ return -1;
+ }
+
+ if (check_modem_rate(general_options.modems, general_options.minrate)) {
+ ast_fax_modem_to_str(general_options.modems, modems, sizeof(modems));
+ ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %d\n", modems, general_options.minrate);
+ return -1;
+ }
+
+ if (check_modem_rate(general_options.modems, general_options.maxrate)) {
+ ast_fax_modem_to_str(general_options.modems, modems, sizeof(modems));
+ ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %d\n", modems, general_options.maxrate);
+ return -1;
+ }
+
return 0;
}
diff --git a/res/res_stun_monitor.c b/res/res_stun_monitor.c
new file mode 100644
index 000000000..54be1e44c
--- /dev/null
+++ b/res/res_stun_monitor.c
@@ -0,0 +1,311 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * David Vossel <dvossel@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief STUN Network Monitor
+ *
+ * \author David Vossel <dvossel@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/event.h"
+#include "asterisk/sched.h"
+#include "asterisk/config.h"
+#include "asterisk/stun.h"
+#include "asterisk/netsock2.h"
+#include "asterisk/lock.h"
+#include <fcntl.h>
+
+static const int STANDARD_STUN_PORT = 3478;
+static const int DEFAULT_MONITOR_REFRESH = 30;
+
+static const char stun_conf_file[] = "res_stun_monitor.conf";
+static struct ast_sched_thread *sched;
+
+static struct {
+ struct sockaddr_in stunaddr; /*!< The stun address we send requests to*/
+ struct sockaddr_in externaladdr; /*!< current perceived external address. */
+ ast_mutex_t lock;
+ unsigned int refresh;
+ int stunsock;
+ unsigned int monitor_enabled:1;
+ unsigned int externaladdr_known:1;
+} args;
+
+static inline void stun_close_sock(void)
+{
+ if (args.stunsock != -1) {
+ close(args.stunsock);
+ args.stunsock = -1;
+ memset(&args.externaladdr, 0, sizeof(args.externaladdr));
+ args.externaladdr_known = 0;
+ }
+}
+
+/* \brief purge the stun socket's receive buffer before issuing a new request
+ *
+ * XXX Note that this is somewhat of a hack. This function is essentially doing
+ * a cleanup on the socket rec buffer to handle removing any STUN responses we have not
+ * handled. This is called before sending out a new STUN request so we don't read
+ * a latent previous response thinking it is new.
+ */
+static void stun_purge_socket(void)
+{
+ int flags = fcntl(args.stunsock, F_GETFL);
+ int res = 0;
+ unsigned char reply_buf[1024];
+
+ fcntl(args.stunsock, F_SETFL, flags | O_NONBLOCK);
+ while (res != -1) {
+ /* throw away everything in the buffer until we reach the end. */
+ res = recv(args.stunsock, reply_buf, sizeof(reply_buf), 0);
+ }
+ fcntl(args.stunsock, F_SETFL, flags & ~O_NONBLOCK);
+}
+
+/* \brief called by scheduler to send STUN request */
+static int stun_monitor_request(const void *blarg)
+{
+ int res;
+ int generate_event = 0;
+ struct sockaddr_in answer = { 0, };
+
+
+ /* once the stun socket goes away, this scheduler item will go away as well */
+ ast_mutex_lock(&args.lock);
+ if (args.stunsock == -1) {
+ ast_log(LOG_ERROR, "STUN monitor: can not send STUN request, socket is not open\n");
+ goto monitor_request_cleanup;
+ }
+
+ stun_purge_socket();
+
+ if (!(ast_stun_request(args.stunsock, &args.stunaddr, NULL, &answer)) &&
+ (memcmp(&args.externaladdr, &answer, sizeof(args.externaladdr)))) {
+ const char *newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
+ int newport = ntohs(answer.sin_port);
+
+ ast_log(LOG_NOTICE, "STUN MONITOR: Old external address/port %s:%d now seen as %s:%d \n",
+ ast_inet_ntoa(args.externaladdr.sin_addr), ntohs(args.externaladdr.sin_port),
+ newaddr, newport);
+
+ memcpy(&args.externaladdr, &answer, sizeof(args.externaladdr));
+
+ if (args.externaladdr_known) {
+ /* the external address was already known, and has changed... generate event. */
+ generate_event = 1;
+
+ } else {
+ /* this was the first external address we found, do not alert listeners
+ * until this address changes to something else. */
+ args.externaladdr_known = 1;
+ }
+ }
+
+ if (generate_event) {
+ struct ast_event *event = ast_event_new(AST_EVENT_NETWORK_CHANGE, AST_EVENT_IE_END);
+ if (!event) {
+ ast_log(LOG_ERROR, "STUN monitor: could not create AST_EVENT_NETWORK_CHANGE event.\n");
+ goto monitor_request_cleanup;
+ }
+ if (ast_event_queue(event)) {
+ ast_event_destroy(event);
+ event = NULL;
+ ast_log(LOG_ERROR, "STUN monitor: could not queue AST_EVENT_NETWORK_CHANGE event.\n");
+ goto monitor_request_cleanup;
+ }
+ }
+
+monitor_request_cleanup:
+ /* always refresh this scheduler item. It will be removed elsewhere when
+ * it is supposed to go away */
+ res = args.refresh * 1000;
+ ast_mutex_unlock(&args.lock);
+
+ return res;
+}
+
+/* \brief stops the stun monitor thread
+ * \note do not hold the args->lock while calling this
+ */
+static void stun_stop_monitor(void)
+{
+ if (sched) {
+ sched = ast_sched_thread_destroy(sched);
+ ast_log(LOG_NOTICE, "STUN monitor stopped\n");
+ }
+ /* it is only safe to destroy the socket without holding arg->lock
+ * after the sched thread is destroyed */
+ stun_close_sock();
+}
+
+/* \brief starts the stun monitor thread
+ * \note The args->lock MUST be held when calling this function
+ */
+static int stun_start_monitor(void)
+{
+ struct ast_sockaddr dst;
+ /* clean up any previous open socket */
+ stun_close_sock();
+
+ /* create destination ast_sockaddr */
+ ast_sockaddr_from_sin(&dst, &args.stunaddr);
+
+ /* open new socket binding */
+ args.stunsock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (args.stunsock < 0) {
+ ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (ast_connect(args.stunsock, &dst) != 0) {
+ ast_log(LOG_WARNING, "SIP STUN Failed to connect to %s\n", ast_sockaddr_stringify(&dst));
+ stun_close_sock();
+ return -1;
+ }
+
+ /* if scheduler thread is not started, make sure to start it now */
+ if (sched) {
+ return 0; /* already started */
+ }
+
+ if (!(sched = ast_sched_thread_create())) {
+ ast_log(LOG_ERROR, "Failed to create stun monitor scheduler thread\n");
+ stun_close_sock();
+ return -1;
+ }
+
+ if (ast_sched_thread_add_variable(sched, (args.refresh * 1000), stun_monitor_request, NULL, 1) < 0) {
+ ast_log(LOG_ERROR, "Unable to schedule STUN network monitor \n");
+ sched = ast_sched_thread_destroy(sched);
+ stun_close_sock();
+ return -1;
+ }
+
+ ast_log(LOG_NOTICE, "STUN monitor started\n");
+ return 0;
+}
+
+static int load_config(int startup)
+{
+ struct ast_flags config_flags = { 0, };
+ struct ast_config *cfg;
+ struct ast_variable *v;
+
+ if (!startup) {
+ ast_set_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
+ }
+
+ if (!(cfg = ast_config_load2(stun_conf_file, "res_stun_monitor", config_flags)) ||
+ cfg == CONFIG_STATUS_FILEINVALID) {
+ ast_log(LOG_ERROR, "Unable to load config %s\n", stun_conf_file);
+ return -1;
+ }
+
+ if (cfg == CONFIG_STATUS_FILEUNCHANGED && !startup) {
+ return 0;
+ }
+
+ /* set defaults */
+ args.monitor_enabled = 0;
+ memset(&args.stunaddr, 0, sizeof(args.stunaddr));
+ args.refresh = DEFAULT_MONITOR_REFRESH;
+
+ for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
+ if (!strcasecmp(v->name, "stunaddr")) {
+ args.stunaddr.sin_port = htons(STANDARD_STUN_PORT);
+ if (ast_parse_arg(v->value, PARSE_INADDR, &args.stunaddr)) {
+ ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", v->value);
+ } else {
+ ast_log(LOG_NOTICE, "STUN monitor enabled: %s\n", v->value);
+ args.monitor_enabled = 1;
+ }
+ } else if (!strcasecmp(v->name, "stunrefresh")) {
+ if ((sscanf(v->value, "%30u", &args.refresh) != 1) || !args.refresh) {
+ ast_log(LOG_WARNING, "Invalid stunrefresh value '%s', must be an integer > 0 at line %d\n", v->value, v->lineno);
+ args.refresh = DEFAULT_MONITOR_REFRESH;
+ } else {
+ ast_log(LOG_NOTICE, "STUN Monitor set to refresh every %d seconds\n", args.refresh);
+ }
+ } else {
+ ast_log(LOG_WARNING, "SIP STUN: invalid config option %s at line %d\n", v->value, v->lineno);
+ }
+ }
+
+ ast_config_destroy(cfg);
+
+ return 0;
+}
+
+static int __reload(int startup)
+{
+ int res;
+
+ ast_mutex_lock(&args.lock);
+ if (!(res = load_config(startup)) && args.monitor_enabled) {
+ res = stun_start_monitor();
+ }
+ ast_mutex_unlock(&args.lock);
+
+ if ((res == -1) || !args.monitor_enabled) {
+ args.monitor_enabled = 0;
+ stun_stop_monitor();
+ }
+
+ return res;
+}
+
+static int reload(void)
+{
+ return __reload(0);
+}
+
+static int unload_module(void)
+{
+ stun_stop_monitor();
+ ast_mutex_destroy(&args.lock);
+ return 0;
+}
+
+static int load_module(void)
+{
+ ast_mutex_init(&args.lock);
+ args.stunsock = -1;
+ memset(&args.externaladdr, 0, sizeof(args.externaladdr));
+ args.externaladdr_known = 0;
+ sched = NULL;
+ if (__reload(1)) {
+ stun_stop_monitor();
+ ast_mutex_destroy(&args.lock);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "STUN Network Monitor",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND
+ );