Newer
Older
17001
17002
17003
17004
17005
17006
17007
17008
17009
17010
17011
17012
17013
17014
17015
17016
17017
17018
17019
17020
17021
17022
17023
17024
/* Check AUDIO RTP keepalives */
if (dialog->lastrtptx && ast_rtp_get_rtpkeepalive(dialog->rtp) &&
(t > dialog->lastrtptx + ast_rtp_get_rtpkeepalive(dialog->rtp))) {
/* Need to send an empty RTP packet */
dialog->lastrtptx = time(NULL);
ast_rtp_sendcng(dialog->rtp, 0);
}
/*! \todo Check video RTP keepalives
Do we need to move the lastrtptx to the RTP structure to have one for audio and one
for video? It really does belong to the RTP structure.
*/
/* Check AUDIO RTP timers */
if (dialog->lastrtprx && (ast_rtp_get_rtptimeout(dialog->rtp) || ast_rtp_get_rtpholdtimeout(dialog->rtp)) &&
(t > dialog->lastrtprx + ast_rtp_get_rtptimeout(dialog->rtp))) {
/* Might be a timeout now -- see if we're on hold */
struct sockaddr_in sin;
ast_rtp_get_peer(dialog->rtp, &sin);
if (sin.sin_addr.s_addr || (ast_rtp_get_rtpholdtimeout(dialog->rtp) &&
(t > dialog->lastrtprx + ast_rtp_get_rtpholdtimeout(dialog->rtp)))) {
/* Needs a hangup */
Joshua Colp
committed
if (ast_rtp_get_rtptimeout(dialog->rtp)) {
while (dialog->owner && ast_channel_trylock(dialog->owner)) {
sip_pvt_unlock(dialog);
usleep(1);
sip_pvt_lock(dialog);
}
ast_log(LOG_NOTICE, "Disconnecting call '%s' for lack of RTP activity in %ld seconds\n",
dialog->owner->name, (long) (t - dialog->lastrtprx));
/* Issue a softhangup */
ast_softhangup_nolock(dialog->owner, AST_SOFTHANGUP_DEV);
ast_channel_unlock(dialog->owner);
/* forget the timeouts for this call, since a hangup
has already been requested and we don't want to
repeatedly request hangups
*/
ast_rtp_set_rtptimeout(dialog->rtp, 0);
ast_rtp_set_rtpholdtimeout(dialog->rtp, 0);
if (dialog->vrtp) {
ast_rtp_set_rtptimeout(dialog->vrtp, 0);
ast_rtp_set_rtpholdtimeout(dialog->vrtp, 0);
}
}
}
}
}
/*! \brief The SIP monitoring thread
\note This thread monitors all the SIP sessions and peers that needs notification of mwi
(and thus do not have a separate thread) indefinitely
*/
static void *do_monitor(void *data)
{
int res;
struct sip_pvt *dialog;
/* Add an I/O event to our SIP UDP socket */
sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL);
/* From here on out, we die whenever asked */
for(;;) {
/* Check for a reload request */
ast_mutex_lock(&sip_reload_lock);
reloading = sip_reloading;
ast_mutex_unlock(&sip_reload_lock);
if (reloading) {
ast_verb(1, "Reloading SIP\n");
sip_do_reload(sip_reloadreason);
/* Change the I/O fd of our UDP socket */
if (sipsock > -1) {
if (sipsock_read_id)
sipsock_read_id = ast_io_change(io, sipsock_read_id, sipsock, NULL, 0, NULL);
else
sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL);
} else if (sipsock_read_id) {
ast_io_remove(io, sipsock_read_id);
sipsock_read_id = NULL;
Olle Johansson
committed
/* Check for dialogs needing to be killed */
t = time(NULL);
Olle Johansson
committed
/* don't scan the dialogs list if it hasn't been a reasonable period
of time since the last time we did it (when MWI is being sent, we can
get back to this point every millisecond or less)
*/
for (dialog = dialoglist; dialog; dialog = dialog->next) {
sip_pvt_lock(dialog);
/* Check RTP timeouts and kill calls if we have a timeout set and do not get RTP */
check_rtp_timeout(dialog, t);
/* If we have sessions that needs to be destroyed, do it now */
/* Check if we have outstanding requests not responsed to or an active call
- if that's the case, wait with destruction */
if (dialog->needdestroy && !dialog->packets && !dialog->owner) {
sip_pvt_unlock(dialog);
__sip_destroy(dialog, TRUE, FALSE);
sip_pvt_unlock(dialog);
pthread_testcancel();
/* Wait for sched or io */
res = ast_sched_wait(sched);
if ((res < 0) || (res > 1000))
res = 1000;
if (res > 20)
ast_debug(1, "chan_sip: ast_io_wait ran %d all at once\n", res);
ast_mutex_lock(&monlock);
if (res >= 0) {
res = ast_sched_runq(sched);
if (res >= 20)
ast_debug(1, "chan_sip: ast_sched_runq ran %d all at once\n", res);
}
ast_mutex_unlock(&monlock);
/* Never reached */
return NULL;
}
/*! \brief Start the channel monitor thread */
static int restart_monitor(void)
{
/* If we're supposed to be stopped -- stay stopped */
if (monitor_thread == AST_PTHREADT_STOP)
ast_mutex_lock(&monlock);
ast_mutex_unlock(&monlock);
ast_log(LOG_WARNING, "Cannot kill myself\n");
return -1;
}
if (monitor_thread != AST_PTHREADT_NULL) {
/* Wake up the thread */
pthread_kill(monitor_thread, SIGURG);
} else {
/* Start a new monitor */
if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
ast_mutex_unlock(&monlock);
ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
return -1;
}
}
ast_mutex_unlock(&monlock);
/*! \brief React to lack of answer to Qualify poke */
static int sip_poke_noanswer(const void *data)
struct sip_peer *peer = (struct sip_peer *)data;
if (peer->lastms > -1) {
ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE! Last qualify: %d\n", peer->name, peer->lastms);
Olle Johansson
committed
manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unreachable\r\nTime: %d\r\n", peer->name, -1);
Olle Johansson
committed
if (global_regextenonqualify)
register_peer_exten(peer, FALSE);
peer->call = sip_destroy(peer->call);
ast_device_state_changed("SIP/%s", peer->name);
peer->pokeexpire = ast_sched_replace(peer->pokeexpire, sched,
DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer);
/*! \brief Check availability of peer, also keep NAT open
\note This is done with the interval in qualify= configuration option
Default is 2 seconds */
static int sip_poke_peer(struct sip_peer *peer)
{
struct sip_pvt *p;
Olle Johansson
committed
int xmitres = 0;
if (!peer->maxms || !peer->addr.sin_addr.s_addr) {
/* IF we have no IP, or this isn't to be monitored, return
imeediately after clearing things out */
if (peer->pokeexpire > -1)
ast_sched_del(sched, peer->pokeexpire);
peer->lastms = 0;
peer->pokeexpire = -1;
peer->call = NULL;
return 0;
}
Russell Bryant
committed
if (sipdebug)
ast_log(LOG_NOTICE, "Still have a QUALIFY dialog active, deleting\n");
peer->call = sip_destroy(peer->call);
if (!(p = peer->call = sip_alloc(NULL, NULL, 0, SIP_OPTIONS)))
p->sa = peer->addr;
p->recv = peer->addr;
ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
/* Send OPTIONs to peer's fullcontact */
if (!ast_strlen_zero(peer->fullcontact))
ast_string_field_set(p, fullcontact, peer->fullcontact);
if (!ast_strlen_zero(peer->tohost))
ast_string_field_set(p, tohost, peer->tohost);
Russell Bryant
committed
else
ast_string_field_set(p, tohost, ast_inet_ntoa(peer->addr.sin_addr));
/* Recalculate our side, and recalculate Call ID */
ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
build_via(p);
build_callid_pvt(p);
if (peer->pokeexpire > -1)
ast_sched_del(sched, peer->pokeexpire);
Kevin P. Fleming
committed
p->relatedpeer = peer;
ast_set_flag(&p->flags[0], SIP_OUTGOING);
Kevin P. Fleming
committed
ast_copy_string(p->username, "__VOCAL_DATA_SHOULD_READ_THE_SIP_SPEC__", sizeof(p->username));
Olle Johansson
committed
xmitres = transmit_invite(p, SIP_INVITE, 0, 2);
Olle Johansson
committed
xmitres = transmit_invite(p, SIP_OPTIONS, 0, 2);
Tilghman Lesher
committed
peer->ps = ast_tvnow();
Olle Johansson
committed
sip_poke_noanswer(peer); /* Immediately unreachable, network problems */
peer->pokeexpire = ast_sched_replace(peer->pokeexpire, sched,
peer->maxms * 2, sip_poke_noanswer, peer);
/*! \brief Part of PBX channel interface
\note
\par Return values:---
Kevin P. Fleming
committed
If we have qualify on and the device is not reachable, regardless of registration
state we return AST_DEVICE_UNAVAILABLE
For peers with call limit:
- not registered AST_DEVICE_UNAVAILABLE
- registered, no call AST_DEVICE_NOT_INUSE
- registered, active calls AST_DEVICE_INUSE
- registered, call limit reached AST_DEVICE_BUSY
- registered, onhold AST_DEVICE_ONHOLD
- registered, ringing AST_DEVICE_RINGING
Kevin P. Fleming
committed
For peers without call limit:
- not registered AST_DEVICE_UNAVAILABLE
- registered AST_DEVICE_NOT_INUSE
- fixed IP (!dynamic) AST_DEVICE_NOT_INUSE
Peers that does not have a known call and can't be reached by OPTIONS
- unreachable AST_DEVICE_UNAVAILABLE
If we return AST_DEVICE_UNKNOWN, the device state engine will try to find
out a state by walking the channel list.
The queue system (\ref app_queue.c) treats a member as "active"
if devicestate is != AST_DEVICE_UNAVAILBALE && != AST_DEVICE_INVALID
When placing a call to the queue member, queue system sets a member to busy if
!= AST_DEVICE_NOT_INUSE and != AST_DEVICE_UNKNOWN
Kevin P. Fleming
committed
*/
static int sip_devicestate(void *data)
{
char *host;
char *tmp;
struct sip_peer *p;
int res = AST_DEVICE_INVALID;
/* make sure data is not null. Maybe unnecessary, but better be safe */
host = ast_strdupa(data ? data : "");
if ((tmp = strchr(host, '@')))
host = tmp + 1;
ast_debug(3, "Checking device state for peer %s\n", host);
if ((p = find_peer(host, NULL, 1))) {
if (p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) {
/* we have an address for the peer */
/* Check status in this order
- Hold
- Ringing
- Busy (enforced only by call limit)
- Inuse (we have a call)
- Unreachable (qualify)
If we don't find any of these state, report AST_DEVICE_NOT_INUSE
for registered devices */
if (p->onHold)
/* First check for hold or ring states */
res = AST_DEVICE_ONHOLD;
else if (p->inRinging) {
if (p->inRinging == p->inUse)
res = AST_DEVICE_RINGING;
else
res = AST_DEVICE_RINGINUSE;
} else if (p->call_limit && (p->inUse == p->call_limit))
/* check call limit */
res = AST_DEVICE_BUSY;
else if (p->call_limit && p->busy_level && p->inUse >= p->busy_level)
/* We're forcing busy before we've reached the call limit */
res = AST_DEVICE_BUSY;
else if (p->call_limit && p->inUse)
/* Not busy, but we do have a call */
res = AST_DEVICE_INUSE;
else if (p->maxms && ((p->lastms > p->maxms) || (p->lastms < 0)))
/* We don't have a call. Are we reachable at all? Requires qualify= */
res = AST_DEVICE_UNAVAILABLE;
else /* Default reply if we're registered and have no other data */
res = AST_DEVICE_NOT_INUSE;
} else {
/* there is no address, it's unavailable */
res = AST_DEVICE_UNAVAILABLE;
} else {
Joshua Colp
committed
res = AST_DEVICE_UNKNOWN;
return res;
}
/*! \brief PBX interface function -build SIP pvt structure
SIP calls initiated by the PBX arrive here
SIP Dial string syntax
SIP/exten@host!dnid
or SIP/host/exten!dnid
or SIP/host!dnid
*/
static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause)
struct ast_channel *tmpc = NULL;
char *ext, *host;
char *dnid;
/* mask request with some set of allowed formats.
* XXX this needs to be fixed.
* The original code uses AST_FORMAT_AUDIO_MASK, but it is
* unclear what to use here. We have global_capabilities, which is
* configured from sip.conf, and sip_tech.capabilities, which is
* hardwired to all audio formats.
*/
format &= AST_FORMAT_AUDIO_MASK;
if (!format) {
Mark Spencer
committed
ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format %s while capability is %s\n", ast_getformatname(oldformat), ast_getformatname(global_capability));
*cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL; /* Can't find codec to connect to host */
ast_debug(1, "Asked to create a SIP channel with formats: %s\n", ast_getformatname_multiple(tmp, sizeof(tmp), oldformat));
if (!(p = sip_alloc(NULL, NULL, 0, SIP_INVITE))) {
ast_log(LOG_ERROR, "Unable to build sip pvt data for '%s' (Out of memory or socket error)\n", dest);
*cause = AST_CAUSE_SWITCH_CONGESTION;
p->outgoing_call = TRUE;
if (!(p->options = ast_calloc(1, sizeof(*p->options)))) {
sip_destroy(p);
ast_log(LOG_ERROR, "Unable to build option SIP data structure - Out of memory\n");
*cause = AST_CAUSE_SWITCH_CONGESTION;
Kevin P. Fleming
committed
return NULL;
Kevin P. Fleming
committed
/* Save the destination, the SIP dial string */
Kevin P. Fleming
committed
ast_copy_string(tmp, dest, sizeof(tmp));
/* Find DNID and take it away */
dnid = strchr(tmp, '!');
if (dnid != NULL) {
*dnid++ = '\0';
ast_string_field_set(p, todnid, dnid);
}
/* Find at sign - @ */
*host++ = '\0';
/* We now have
host = peer name, DNS host name or DNS domain (for SRV)
ext = extension (user part of URI)
dnid = destination of the call (applies to the To: header)
*/
Mark Spencer
committed
*cause = AST_CAUSE_UNREGISTERED;
ast_debug(3, "Cant create SIP call - target device not registred\n");
if (ast_strlen_zero(p->peername) && ext)
ast_string_field_set(p, peername, ext);
/* Recalculate our side, and recalculate Call ID */
ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
build_via(p);
build_callid_pvt(p);
/* We have an extension to call, don't use the full contact here */
/* This to enable dialing registered peers with extension dialling,
like SIP/peername/extension
SIP/peername will still use the full contact
*/
if (ext) {
ast_string_field_set(p, username, ext);
ast_string_field_set(p, fullcontact, NULL);
printf("Setting up to call extension '%s' at '%s'\n", ext ? ext : "<none>", host);
p->prefcodec = oldformat; /* Format for this call */
p->jointcapability = oldformat;
sip_pvt_lock(p);
Kevin P. Fleming
committed
tmpc = sip_new(p, AST_STATE_DOWN, host); /* Place the call */
Olle Johansson
committed
if (global_callevents)
manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
"Channel: %s\r\nChanneltype: %s\r\nSIPcallid: %s\r\nSIPfullcontact: %s\r\nPeername: %s\r\n",
p->owner? p->owner->name : "", "SIP", p->callid, p->fullcontact, p->peername);
sip_pvt_unlock(p);
/*! Parse insecure= setting in sip.conf and set flags according to setting */
static void set_insecure_flags (struct ast_flags *flags, const char *value, int lineno)
{
if (ast_strlen_zero(value))
return;
17467
17468
17469
17470
17471
17472
17473
17474
17475
17476
17477
17478
17479
17480
17481
17482
17483
if (!ast_false(value)) {
char buf[64];
char *word, *next;
ast_copy_string(buf, value, sizeof(buf));
next = buf;
while ((word = strsep(&next, ","))) {
if (!strcasecmp(word, "port"))
ast_set_flag(&flags[0], SIP_INSECURE_PORT);
else if (!strcasecmp(word, "invite"))
ast_set_flag(&flags[0], SIP_INSECURE_INVITE);
else
ast_log(LOG_WARNING, "Unknown insecure mode '%s' on line %d\n", value, lineno);
}
}
}
\brief Handle flag-type options common to configuration of devices - users and peers
\param flags array of two struct ast_flags
\param mask array of two struct ast_flags
\param v linked list of config variables to process
\returns non-zero if any config options were handled, zero otherwise
*/
static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v)
{
if (!strcasecmp(v->name, "trustrpid")) {
ast_set_flag(&mask[0], SIP_TRUSTRPID);
ast_set2_flag(&flags[0], ast_true(v->value), SIP_TRUSTRPID);
Kevin P. Fleming
committed
} else if (!strcasecmp(v->name, "sendrpid")) {
ast_set_flag(&mask[0], SIP_SENDRPID);
ast_set2_flag(&flags[0], ast_true(v->value), SIP_SENDRPID);
Kevin P. Fleming
committed
} else if (!strcasecmp(v->name, "g726nonstandard")) {
ast_set_flag(&mask[0], SIP_G726_NONSTANDARD);
ast_set2_flag(&flags[0], ast_true(v->value), SIP_G726_NONSTANDARD);
} else if (!strcasecmp(v->name, "useclientcode")) {
ast_set_flag(&mask[0], SIP_USECLIENTCODE);
ast_set2_flag(&flags[0], ast_true(v->value), SIP_USECLIENTCODE);
} else if (!strcasecmp(v->name, "dtmfmode")) {
ast_set_flag(&mask[0], SIP_DTMF);
ast_clear_flag(&flags[0], SIP_DTMF);
if (!strcasecmp(v->value, "inband"))
ast_set_flag(&flags[0], SIP_DTMF_INBAND);
else if (!strcasecmp(v->value, "rfc2833"))
ast_set_flag(&flags[0], SIP_DTMF_RFC2833);
else if (!strcasecmp(v->value, "info"))
ast_set_flag(&flags[0], SIP_DTMF_INFO);
else if (!strcasecmp(v->value, "shortinfo"))
ast_set_flag(&flags[0], SIP_DTMF_SHORTINFO);
Mark Spencer
committed
else if (!strcasecmp(v->value, "auto"))
ast_set_flag(&flags[0], SIP_DTMF_AUTO);
else {
ast_log(LOG_WARNING, "Unknown dtmf mode '%s' on line %d, using rfc2833\n", v->value, v->lineno);
ast_set_flag(&flags[0], SIP_DTMF_RFC2833);
}
} else if (!strcasecmp(v->name, "nat")) {
ast_set_flag(&mask[0], SIP_NAT);
ast_clear_flag(&flags[0], SIP_NAT);
if (!strcasecmp(v->value, "never"))
ast_set_flag(&flags[0], SIP_NAT_NEVER);
else if (!strcasecmp(v->value, "route"))
ast_set_flag(&flags[0], SIP_NAT_ROUTE);
else if (ast_true(v->value))
ast_set_flag(&flags[0], SIP_NAT_ALWAYS);
ast_set_flag(&flags[0], SIP_NAT_RFC3581);
} else if (!strcasecmp(v->name, "canreinvite")) {
ast_set_flag(&mask[0], SIP_REINVITE);
ast_clear_flag(&flags[0], SIP_REINVITE);
Kevin P. Fleming
committed
17538
17539
17540
17541
17542
17543
17544
17545
17546
17547
17548
17549
17550
17551
17552
17553
17554
17555
if (ast_true(v->value)) {
ast_set_flag(&flags[0], SIP_CAN_REINVITE | SIP_CAN_REINVITE_NAT);
} else if (!ast_false(v->value)) {
char buf[64];
char *word, *next = buf;
ast_copy_string(buf, v->value, sizeof(buf));
while ((word = strsep(&next, ","))) {
if (!strcasecmp(word, "update")) {
ast_set_flag(&flags[0], SIP_REINVITE_UPDATE | SIP_CAN_REINVITE);
} else if (!strcasecmp(word, "nonat")) {
ast_set_flag(&flags[0], SIP_CAN_REINVITE);
ast_clear_flag(&flags[0], SIP_CAN_REINVITE_NAT);
} else {
ast_log(LOG_WARNING, "Unknown canreinvite mode '%s' on line %d\n", v->value, v->lineno);
}
}
}
} else if (!strcasecmp(v->name, "insecure")) {
ast_set_flag(&mask[0], SIP_INSECURE);
ast_clear_flag(&flags[0], SIP_INSECURE);
set_insecure_flags(&flags[0], v->value, v->lineno);
} else if (!strcasecmp(v->name, "progressinband")) {
ast_set_flag(&mask[0], SIP_PROG_INBAND);
ast_clear_flag(&flags[0], SIP_PROG_INBAND);
if (ast_true(v->value))
ast_set_flag(&flags[0], SIP_PROG_INBAND_YES);
else if (strcasecmp(v->value, "never"))
ast_set_flag(&flags[0], SIP_PROG_INBAND_NO);
} else if (!strcasecmp(v->name, "promiscredir")) {
ast_set_flag(&mask[0], SIP_PROMISCREDIR);
ast_set2_flag(&flags[0], ast_true(v->value), SIP_PROMISCREDIR);
} else if (!strcasecmp(v->name, "videosupport")) {
ast_set_flag(&mask[1], SIP_PAGE2_VIDEOSUPPORT);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_VIDEOSUPPORT);
} else if (!strcasecmp(v->name, "textsupport")) {
ast_set_flag(&mask[1], SIP_PAGE2_TEXTSUPPORT);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_TEXTSUPPORT);
res = 1;
} else if (!strcasecmp(v->name, "allowoverlap")) {
ast_set_flag(&mask[1], SIP_PAGE2_ALLOWOVERLAP);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWOVERLAP);
} else if (!strcasecmp(v->name, "allowsubscribe")) {
ast_set_flag(&mask[1], SIP_PAGE2_ALLOWSUBSCRIBE);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWSUBSCRIBE);
} else if (!strcasecmp(v->name, "t38pt_udptl")) {
ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_UDPTL);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_UDPTL);
#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS
} else if (!strcasecmp(v->name, "t38pt_rtp")) {
ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_RTP);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_RTP);
} else if (!strcasecmp(v->name, "t38pt_tcp")) {
ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_TCP);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_TCP);
#endif
} else if (!strcasecmp(v->name, "rfc2833compensate")) {
ast_set_flag(&mask[1], SIP_PAGE2_RFC2833_COMPENSATE);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RFC2833_COMPENSATE);
} else if (!strcasecmp(v->name, "buggymwi")) {
ast_set_flag(&mask[1], SIP_PAGE2_BUGGY_MWI);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_BUGGY_MWI);
return res;
}
/*! \brief Add SIP domain to list of domains we are responsible for */
static int add_sip_domain(const char *domain, const enum domain_mode mode, const char *context)
{
struct domain *d;
if (ast_strlen_zero(domain)) {
ast_log(LOG_WARNING, "Zero length domain.\n");
return 1;
}
if (!(d = ast_calloc(1, sizeof(*d))))
return 0;
ast_copy_string(d->domain, domain, sizeof(d->domain));
if (!ast_strlen_zero(context))
ast_copy_string(d->context, context, sizeof(d->context));
d->mode = mode;
AST_LIST_LOCK(&domain_list);
AST_LIST_INSERT_TAIL(&domain_list, d, list);
AST_LIST_UNLOCK(&domain_list);
Russell Bryant
committed
if (sipdebug)
ast_debug(1, "Added local SIP domain '%s'\n", domain);
return 1;
}
/*! \brief check_sip_domain: Check if domain part of uri is local to our server */
17637
17638
17639
17640
17641
17642
17643
17644
17645
17646
17647
17648
17649
17650
17651
17652
17653
17654
17655
17656
17657
static int check_sip_domain(const char *domain, char *context, size_t len)
{
struct domain *d;
int result = 0;
AST_LIST_LOCK(&domain_list);
AST_LIST_TRAVERSE(&domain_list, d, list) {
if (strcasecmp(d->domain, domain))
continue;
if (len && !ast_strlen_zero(d->context))
ast_copy_string(context, d->context, len);
result = 1;
break;
}
AST_LIST_UNLOCK(&domain_list);
return result;
}
/*! \brief Clear our domain list (at reload) */
static void clear_sip_domains(void)
{
struct domain *d;
AST_LIST_LOCK(&domain_list);
while ((d = AST_LIST_REMOVE_HEAD(&domain_list, list)))
Tilghman Lesher
committed
ast_free(d);
AST_LIST_UNLOCK(&domain_list);
}
/*! \brief Add realm authentication in list */
static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, const char *configuration, int lineno)
char authcopy[256];
char *username=NULL, *realm=NULL, *secret=NULL, *md5secret=NULL;
if (ast_strlen_zero(configuration))
ast_debug(1, "Auth config :: %s\n", configuration);
ast_copy_string(authcopy, configuration, sizeof(authcopy));
stringp = authcopy;
username = stringp;
realm = strrchr(stringp, '@');
if (ast_strlen_zero(username) || ast_strlen_zero(realm)) {
ast_log(LOG_WARNING, "Format for authentication entry is user[:secret]@realm at line %d\n", lineno);
return authlist;
}
stringp = username;
username = strsep(&stringp, ":");
if (username) {
secret = strsep(&stringp, ":");
md5secret = strsep(&stringp,"#");
}
if (!(auth = ast_calloc(1, sizeof(*auth))))
ast_copy_string(auth->realm, realm, sizeof(auth->realm));
ast_copy_string(auth->username, username, sizeof(auth->username));
if (secret)
ast_copy_string(auth->secret, secret, sizeof(auth->secret));
if (md5secret)
ast_copy_string(auth->md5secret, md5secret, sizeof(auth->md5secret));
/* find the end of the list */
for (b = NULL, a = authlist; a ; b = a, a = a->next)
;
if (b)
b->next = auth; /* Add structure add end of list */
else
authlist = auth;
ast_verb(3, "Added authentication for realm %s\n", realm);
/*! \brief Clear realm authentication list (at reload) */
static int clear_realm_authentication(struct sip_auth *authlist)
{
struct sip_auth *a = authlist;
struct sip_auth *b;
while (a) {
b = a;
a = a->next;
Tilghman Lesher
committed
ast_free(b);
/*! \brief Find authentication for a specific realm */
static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, const char *realm)
Russell Bryant
committed
struct sip_auth *a;
Russell Bryant
committed
for (a = authlist; a; a = a->next) {
if (!strcasecmp(a->realm, realm))
/*!
* implement the servar config line
*/
static struct ast_variable *add_var(const char *buf, struct ast_variable *list)
{
struct ast_variable *tmpvar = NULL;
char *varname = ast_strdupa(buf), *varval = NULL;
if ((varval = strchr(varname,'='))) {
*varval++ = '\0';
Steve Murphy
committed
if ((tmpvar = ast_variable_new(varname, varval, ""))) {
tmpvar->next = list;
list = tmpvar;
}
}
return list;
}
/*! \brief Initiate a SIP user structure from configuration (configuration or realtime) */
static struct sip_user *build_user(const char *name, struct ast_variable *v, int realtime)
{
struct sip_user *user;
int format;
struct ast_ha *oldha = NULL;
struct ast_flags userflags[2] = {{(0)}};
struct ast_flags mask[2] = {{(0)}};
Kevin P. Fleming
committed
if (!(user = ast_calloc(1, sizeof(*user))))
suserobjs++;
ASTOBJ_INIT(user);
ast_copy_string(user->name, name, sizeof(user->name));
oldha = user->ha;
user->ha = NULL;
ast_copy_flags(&user->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY);
ast_copy_flags(&user->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
user->capability = global_capability;
user->allowtransfer = global_allowtransfer;
user->maxcallbitrate = default_maxcallbitrate;
user->autoframing = global_autoframing;
Olle Johansson
committed
if (global_callcounter)
user->call_limit=999;
Olle Johansson
committed
user->prefs = default_prefs;
/* set default context */
strcpy(user->context, default_context);
strcpy(user->language, default_language);
Kevin P. Fleming
committed
strcpy(user->mohinterpret, default_mohinterpret);
strcpy(user->mohsuggest, default_mohsuggest);
for (; v; v = v->next) {
if (handle_common_options(&userflags[0], &mask[0], v))
if (!strcasecmp(v->name, "context")) {
ast_copy_string(user->context, v->value, sizeof(user->context));
Kevin P. Fleming
committed
} else if (!strcasecmp(v->name, "subscribecontext")) {
ast_copy_string(user->subscribecontext, v->value, sizeof(user->subscribecontext));
} else if (!strcasecmp(v->name, "setvar")) {
user->chanvars = add_var(v->value, user->chanvars);
} else if (!strcasecmp(v->name, "permit") ||
!strcasecmp(v->name, "deny")) {
int ha_error = 0;
user->ha = ast_append_ha(v->name, v->value, user->ha, &ha_error);
if (ha_error)
ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
} else if (!strcasecmp(v->name, "allowtransfer")) {
user->allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED;
} else if (!strcasecmp(v->name, "secret")) {
ast_copy_string(user->secret, v->value, sizeof(user->secret));
} else if (!strcasecmp(v->name, "md5secret")) {
ast_copy_string(user->md5secret, v->value, sizeof(user->md5secret));
} else if (!strcasecmp(v->name, "callerid")) {
ast_callerid_split(v->value, user->cid_name, sizeof(user->cid_name), user->cid_num, sizeof(user->cid_num));
} else if (!strcasecmp(v->name, "fullname")) {
ast_copy_string(user->cid_name, v->value, sizeof(user->cid_name));
} else if (!strcasecmp(v->name, "cid_number")) {
ast_copy_string(user->cid_num, v->value, sizeof(user->cid_num));
} else if (!strcasecmp(v->name, "callgroup")) {
user->callgroup = ast_get_group(v->value);
} else if (!strcasecmp(v->name, "pickupgroup")) {
user->pickupgroup = ast_get_group(v->value);
} else if (!strcasecmp(v->name, "language")) {
ast_copy_string(user->language, v->value, sizeof(user->language));
} else if (!strcasecmp(v->name, "mohinterpret")) {
Kevin P. Fleming
committed
ast_copy_string(user->mohinterpret, v->value, sizeof(user->mohinterpret));
} else if (!strcasecmp(v->name, "mohsuggest")) {
ast_copy_string(user->mohsuggest, v->value, sizeof(user->mohsuggest));
} else if (!strcasecmp(v->name, "accountcode")) {
ast_copy_string(user->accountcode, v->value, sizeof(user->accountcode));
Olle Johansson
committed
} else if (!strcasecmp(v->name, "callcounter")) {
user->call_limit = ast_true(v->value) ? 999 : 0;
} else if (!strcasecmp(v->name, "call-limit")) {
Kevin P. Fleming
committed
user->call_limit = atoi(v->value);
if (user->call_limit < 0)
user->call_limit = 0;
} else if (!strcasecmp(v->name, "amaflags")) {
format = ast_cdr_amaflags2int(v->value);
if (format < 0) {
ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
} else {
user->amaflags = format;
}
} else if (!strcasecmp(v->name, "allow")) {
int error = ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, TRUE);
if (error)
ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
} else if (!strcasecmp(v->name, "disallow")) {
int error = ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, FALSE);
if (error)
ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
} else if (!strcasecmp(v->name, "autoframing")) {
user->autoframing = ast_true(v->value);
} else if (!strcasecmp(v->name, "callingpres")) {
user->callingpres = ast_parse_caller_presentation(v->value);
if (user->callingpres == -1)
user->callingpres = atoi(v->value);
} else if (!strcasecmp(v->name, "maxcallbitrate")) {
user->maxcallbitrate = atoi(v->value);
if (user->maxcallbitrate < 0)
user->maxcallbitrate = default_maxcallbitrate;
}
/* We can't just report unknown options here because this may be a
* type=friend entry. All user options are valid for a peer, but not
* the other way around. */
ast_copy_flags(&user->flags[0], &userflags[0], mask[0].flags);
ast_copy_flags(&user->flags[1], &userflags[1], mask[1].flags);
if (ast_test_flag(&user->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE))
global_allowsubscribe = TRUE; /* No global ban any more */
/*! \brief Set peer defaults before configuring specific configurations */
static void set_peer_defaults(struct sip_peer *peer)
if (peer->expire == 0) {
/* Don't reset expire or port time during reload
if we have an active registration
*/
peer->expire = -1;
peer->pokeexpire = -1;
peer->addr.sin_port = htons(STANDARD_SIP_PORT);
ast_copy_flags(&peer->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY);
ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
strcpy(peer->context, default_context);
Kevin P. Fleming
committed
strcpy(peer->subscribecontext, default_subscribecontext);
strcpy(peer->language, default_language);
Kevin P. Fleming
committed
strcpy(peer->mohinterpret, default_mohinterpret);
strcpy(peer->mohsuggest, default_mohsuggest);
peer->addr.sin_family = AF_INET;
peer->defaddr.sin_family = AF_INET;
Mark Spencer
committed
peer->capability = global_capability;
Mark Spencer
committed
peer->rtptimeout = global_rtptimeout;
peer->rtpholdtimeout = global_rtpholdtimeout;
peer->rtpkeepalive = global_rtpkeepalive;
peer->allowtransfer = global_allowtransfer;
peer->autoframing = global_autoframing;
Russell Bryant
committed
peer->qualifyfreq = global_qualifyfreq;
Olle Johansson
committed
if (global_callcounter)
peer->call_limit=999;
strcpy(peer->vmexten, default_vmexten);
peer->secret[0] = '\0';
peer->md5secret[0] = '\0';
peer->cid_num[0] = '\0';
peer->cid_name[0] = '\0';
peer->fromdomain[0] = '\0';
peer->fromuser[0] = '\0';
peer->regexten[0] = '\0';
peer->callgroup = 0;
peer->pickupgroup = 0;
peer->maxms = default_qualify;
peer->prefs = default_prefs;
Olle Johansson
committed
peer->timer_t1 = global_t1;
peer->timer_b = global_timer_b;
Russell Bryant
committed
clear_peer_mailboxes(peer);
}
/*! \brief Create temporary peer (used in autocreatepeer mode) */
static struct sip_peer *temp_peer(const char *name)
{
struct sip_peer *peer;
if (!(peer = ast_calloc(1, sizeof(*peer))))
return NULL;
apeerobjs++;
ASTOBJ_INIT(peer);
set_peer_defaults(peer);
ast_copy_string(peer->name, name, sizeof(peer->name));
peer->selfdestruct = TRUE;
peer->host_dynamic = TRUE;
Olle Johansson
committed
peer->prefs = default_prefs;
Russell Bryant
committed
static void add_peer_mailboxes(struct sip_peer *peer, const char *value)
{
char *next, *mbox, *context;
next = ast_strdupa(value);
while ((mbox = context = strsep(&next, ","))) {
struct sip_mailbox *mailbox;
if (!(mailbox = ast_calloc(1, sizeof(*mailbox))))
continue;
strsep(&context, "@");
if (ast_strlen_zero(mbox)) {
ast_free(mailbox);
Russell Bryant
committed
continue;
}
mailbox->mailbox = ast_strdup(mbox);
mailbox->context = ast_strdup(context);
AST_LIST_INSERT_TAIL(&peer->mailboxes, mailbox, entry);
}
}
/*! \brief Build peer from configuration (file or realtime static/dynamic) */
static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime)
struct sip_peer *peer = NULL;
struct ast_ha *oldha = NULL;
int firstpass=1;
int format=0; /* Ama flags */
Kevin P. Fleming
committed
time_t regseconds = 0;
struct ast_flags peerflags[2] = {{(0)}};
struct ast_flags mask[2] = {{(0)}};
Olle Johansson
committed
char callback[256] = "";
Kevin P. Fleming
committed
Mark Spencer
committed
/* Note we do NOT use find_peer here, to avoid realtime recursion */
/* We also use a case-sensitive comparison (unlike find_peer) so
that case changes made to the peer name will be properly handled
during reload