Newer
Older
/* New SIP request coming in
(could be new request in existing SIP dialog as well...)
*/
p->method = req->method; /* Find out which SIP method they are using */
if (option_debug > 3)
ast_log(LOG_DEBUG, "**** Received %s (%d) - Command in SIP %s\n", sip_methods[p->method].text, sip_methods[p->method].id, cmd);
if (p->icseq && (p->icseq > seqno)) {
if (option_debug)
ast_log(LOG_DEBUG, "Ignoring too old SIP packet packet %d (expecting >= %d)\n", seqno, p->icseq);
if (req->method != SIP_ACK)
transmit_response(p, "503 Server error", req); /* We must respond according to RFC 3261 sec 12.2 */
} else if (p->icseq &&
p->icseq == seqno &&
req->method != SIP_ACK &&
(p->method != SIP_CANCEL || ast_test_flag(&p->flags[0], SIP_ALREADYGONE))) {
/* ignore means "don't do anything with it" but still have to
respond appropriately. We do this if we receive a repeat of
the last sequence number */
ignore = 2;
ast_set_flag(req, SIP_PKT_IGNORE);
ast_set_flag(req, SIP_PKT_IGNORE_REQ);
if (option_debug > 2)
ast_log(LOG_DEBUG, "Ignoring SIP message because of retransmit (%s Seqno %d, ours %d)\n", sip_methods[p->method].text, p->icseq, seqno);
if (seqno >= p->icseq)
/* Next should follow monotonically (but not necessarily
incrementally -- thanks again to the genius authors of SIP --
increasing */
p->icseq = seqno;
/* Find their tag if we haven't got it */
if (ast_strlen_zero(p->theirtag)) {
char tag[128];
gettag(req, "From", tag, sizeof(tag));
ast_string_field_set(p, theirtag, tag);
}
snprintf(p->lastmsg, sizeof(p->lastmsg), "Rx: %s", cmd);
if (pedanticsipchecking) {
/* If this is a request packet without a from tag, it's not
correct according to RFC 3261 */
/* Check if this a new request in a new dialog with a totag already attached to it,
RFC 3261 - section 12.2 - and we don't want to mess with recovery */
if (!p->initreq.headers && ast_test_flag(req, SIP_PKT_WITH_TOTAG)) {
/* If this is a first request and it got a to-tag, it is not for us */
if (!ast_test_flag(req, SIP_PKT_IGNORE) && req->method == SIP_INVITE) {
transmit_response_reliable(p, "481 Call/Transaction Does Not Exist", req);
/* Will cease to exist after ACK */
transmit_response(p, "481 Call/Transaction Does Not Exist", req);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
}
return res;
}
}
/* Handle various incoming SIP methods in requests */
switch (p->method) {
case SIP_OPTIONS:
res = handle_request_options(p, req);
break;
case SIP_INVITE:
res = handle_request_invite(p, req, debug, seqno, sin, recount, e);
break;
case SIP_REFER:
res = handle_request_refer(p, req, debug, ignore, seqno, nounlock);
break;
case SIP_CANCEL:
res = handle_request_cancel(p, req);
break;
case SIP_BYE:
res = handle_request_bye(p, req);
break;
case SIP_MESSAGE:
res = handle_request_message(p, req);
break;
case SIP_SUBSCRIBE:
res = handle_request_subscribe(p, req, sin, seqno, e);
break;
case SIP_REGISTER:
res = handle_request_register(p, req, sin, e);
break;
case SIP_INFO:
if (ast_test_flag(req, SIP_PKT_DEBUG))
ast_verbose("Receiving INFO!\n");
if (!ignore)
handle_request_info(p, req);
else /* if ignoring, transmit response */
transmit_response(p, "200 OK", req);
break;
case SIP_NOTIFY:
res = handle_request_notify(p, req, sin, seqno, e);
break;
case SIP_ACK:
/* Make sure we don't ignore this */
if (seqno == p->pendinginvite) {
p->pendinginvite = 0;
__sip_ack(p, seqno, FLAG_RESPONSE, 0, FALSE);
if (find_sdp(req)) {
if (process_sdp(p, req))
return -1;
}
check_pendings(p);
}
if (!p->lastinvite && ast_strlen_zero(p->randdata))
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
break;
default:
Kevin P. Fleming
committed
transmit_response_with_allow(p, "501 Method Not Implemented", req, 0);
ast_log(LOG_NOTICE, "Unknown SIP command '%s' from '%s'\n",
Russell Bryant
committed
cmd, ast_inet_ntoa(p->sa.sin_addr));
/* If this is some new method, and we don't have a call, destroy it now */
if (!p->initreq.headers)
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
\note sipsock_read locks the owner channel while we are processing the SIP message
\return 1 on error, 0 on success
\note Successful messages is connected to SIP call and forwarded to handle_request()
*/
static int sipsock_read(int *id, int fd, short events, void *ignore)
{
struct sip_request req;
socklen_t len;
Mark Spencer
committed
int nounlock;
unsigned int lockretry = 100;
res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len);
if (res < 0) {
if (errno == EAGAIN)
ast_log(LOG_NOTICE, "SIP: Received packet with bad UDP checksum\n");
else
#endif
if (errno != ECONNREFUSED)
ast_log(LOG_WARNING, "Recv error: %s\n", strerror(errno));
return 1;
}
if (option_debug && res == sizeof(req.data)) {
ast_log(LOG_DEBUG, "Received packet exceeds buffer. Data is possibly lost\n");
req.data[sizeof(req.data) - 1] = '\0';
} else
req.data[res] = '\0';
Olle Johansson
committed
if(sip_debug_test_addr(&sin)) /* Set the debug flag early on packet level */
ast_set_flag(&req, SIP_PKT_DEBUG);
if (pedanticsipchecking)
req.len = lws2sws(req.data, req.len); /* Fix multiline headers */
Olle Johansson
committed
if (ast_test_flag(&req, SIP_PKT_DEBUG))
Russell Bryant
committed
ast_verbose("\n<-- SIP read from %s:%d: \n%s\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), req.data);
Olle Johansson
committed
parse_request(&req);
req.method = find_sip_method(req.rlPart1);
if (ast_test_flag(&req, SIP_PKT_DEBUG)) {
ast_verbose("--- (%d headers %d lines)", req.headers, req.lines);
if (req.headers + req.lines == 0)
ast_verbose(" Nat keepalive ");
ast_verbose("---\n");
}
if (req.headers < 2) {
/* Must have at least two headers */
return 1;
}
Kevin P. Fleming
committed
/* Process request, with netlock held */
ast_mutex_lock(&netlock);
/* Find the active SIP dialog or create a new one */
p = find_call(&req, &sin, req.method); /* returns p locked */
/* Go ahead and lock the owner if it has one -- we may need it */
/* becaues this is deadlock-prone, we need to try and unlock if failed */
if (p->owner && ast_channel_trylock(p->owner)) {
Olle Johansson
committed
if (option_debug)
ast_log(LOG_DEBUG, "Failed to grab owner channel lock, trying again. (SIP call %s)\n", p->callid);
ast_mutex_unlock(&p->lock);
/* Sleep for a very short amount of time */
BJ Weschke
committed
goto retrylock;
}
p->recv = sin;
Olle Johansson
committed
if (recordhistory) /* This is a request or response, note what it was for */
append_history(p, "Rx", "%s / %s / %s", req.data, get_header(&req, "CSeq"), req.rlPart2);
if (!lockretry) {
ast_log(LOG_ERROR, "We could NOT get the channel lock for %s! \n", p->owner->name ? p->owner->name : "- no channel name ??? - ");
ast_log(LOG_ERROR, "SIP transaction failed: %s \n", p->callid);
transmit_response(p, "503 Server error", &req); /* We must respond according to RFC 3261 sec 12.2 */
/* XXX We could add retry-after to make sure they come back */
append_history(p, "LockFail", "Owner lock failed, transaction failed.");
return 1;
}
Mark Spencer
committed
nounlock = 0;
if (handle_request(p, &req, &sin, &recount, &nounlock) == -1) {
/* Request failed */
Olle Johansson
committed
if (option_debug)
ast_log(LOG_DEBUG, "SIP message could not be handled, bad request: %-70.70s\n", p->callid[0] ? p->callid : "<no callid>");
}
Mark Spencer
committed
if (p->owner && !nounlock)
ast_mutex_unlock(&p->lock);
Olle Johansson
committed
} else {
if (option_debug)
ast_log(LOG_DEBUG, "Invalid SIP message - rejected , bad request: %-70.70s\n", p->callid[0] ? p->callid : "<no callid>");
ast_mutex_unlock(&netlock);
if (recount)
ast_update_use_count();
/*! \brief Send message waiting indication to alert peer that they've got voicemail */
static int sip_send_mwi_to_peer(struct sip_peer *peer)
{
/* Called with peerl lock, but releases it */
struct sip_pvt *p;
Tilghman Lesher
committed
ast_app_inboxcount(peer->mailbox, &newmsgs, &oldmsgs);
peer->lastmsgcheck = time(NULL);
/* Return now if it's the same thing we told them last time */
if (((newmsgs << 8) | (oldmsgs)) == peer->lastmsgssent) {
peer->lastmsgssent = ((newmsgs << 8) | (oldmsgs));
Kevin P. Fleming
committed
if (peer->mwipvt) {
/* Base message on subscription */
p = peer->mwipvt;
} else {
/* Build temporary dialog for this message */
Olle Johansson
committed
if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY)))
Kevin P. Fleming
committed
return -1;
if (create_addr_from_peer(p, peer)) {
/* Maybe they're not registered, etc. */
sip_destroy(p);
return 0;
}
/* Recalculate our side, and recalculate Call ID */
if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip))
p->ourip = __ourip;
Kevin P. Fleming
committed
build_via(p);
build_callid_pvt(p);
/* Destroy this session after 32 secs */
sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
ast_set_flag(&p->flags[0], SIP_OUTGOING);
Kevin P. Fleming
committed
transmit_notify_with_mwi(p, newmsgs, oldmsgs, peer->vmexten);
Kevin P. Fleming
committed
/*! \brief Check whether peer needs a new MWI notification check */
static int does_peer_need_mwi(struct sip_peer *peer)
{
time_t t = time(NULL);
Kevin P. Fleming
committed
if (ast_test_flag(&peer->flags[1], SIP_PAGE2_SUBSCRIBEMWIONLY) &&
!peer->mwipvt) { /* We don't have a subscription */
peer->lastmsgcheck = t; /* Reset timer */
Kevin P. Fleming
committed
return FALSE;
}
if (!ast_strlen_zero(peer->mailbox) && (t - peer->lastmsgcheck) > global_mwitime)
Kevin P. Fleming
committed
return TRUE;
return FALSE;
}
/*! \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 *sip;
struct sip_peer *peer = NULL;
int lastpeernum = -1;
int curpeernum;
/* Add an I/O event to our SIP UDP socket */
if (sipsock > -1)
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) {
if (option_verbose > 0)
ast_verbose(VERBOSE_PREFIX_1 "Reloading SIP\n");
sip_do_reload(sip_reloadreason);
/* Check for interfaces needing to be killed */
ast_mutex_lock(&iflock);
t = time(NULL);
/* don't scan the interface 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 (sip = iflist; !fastrestart && sip; sip = sip->next) {
ast_mutex_lock(&sip->lock);
/* Check RTP timeouts and kill calls if we have a timeout set and do not get RTP */
if (sip->rtp && sip->owner &&
(sip->owner->_state == AST_STATE_UP) &&
!sip->redirip.sin_addr.s_addr) {
if (sip->lastrtptx &&
sip->rtpkeepalive &&
(t > sip->lastrtptx + sip->rtpkeepalive)) {
/* Need to send an empty RTP packet */
sip->lastrtptx = time(NULL);
ast_rtp_sendcng(sip->rtp, 0);
}
if (sip->lastrtprx &&
(sip->rtptimeout || sip->rtpholdtimeout) &&
(t > sip->lastrtprx + sip->rtptimeout)) {
/* Might be a timeout now -- see if we're on hold */
struct sockaddr_in sin;
ast_rtp_get_peer(sip->rtp, &sin);
if (sin.sin_addr.s_addr ||
(sip->rtpholdtimeout &&
(t > sip->lastrtprx + sip->rtpholdtimeout))) {
/* Needs a hangup */
if (sip->rtptimeout) {
while (sip->owner && ast_channel_trylock(sip->owner)) {
ast_mutex_unlock(&sip->lock);
usleep(1);
ast_mutex_lock(&sip->lock);
}
if (sip->owner) {
Joshua Colp
committed
if (!(ast_rtp_get_bridged(sip->rtp))) {
ast_log(LOG_NOTICE,
"Disconnecting call '%s' for lack of RTP activity in %ld seconds\n",
sip->owner->name,
(long) (t - sip->lastrtprx));
/* Issue a softhangup */
ast_softhangup_nolock(sip->owner, AST_SOFTHANGUP_DEV);
} else
ast_log(LOG_NOTICE, "'%s' will not be disconnected in %ld seconds because it is directly bridged to another RTP stream\n", sip->owner->name, (long) (t - sip->lastrtprx));
/* forget the timeouts for this call, since a hangup
has already been requested and we don't want to
repeatedly request hangups
*/
sip->rtptimeout = 0;
sip->rtpholdtimeout = 0;
}
}
}
}
}
/* If we have sessions that needs to be destroyed, do it now */
if (ast_test_flag(&sip->flags[0], SIP_NEEDDESTROY) && !sip->packets &&
!sip->owner) {
ast_mutex_unlock(&sip->lock);
ast_mutex_unlock(&sip->lock);
ast_mutex_unlock(&iflock);
pthread_testcancel();
/* Wait for sched or io */
res = ast_sched_wait(sched);
if ((res < 0) || (res > 1000))
res = 1000;
/* If we might need to send more mailboxes, don't wait long at all.*/
if (fastrestart)
res = 1;
ast_log(LOG_DEBUG, "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 (option_debug && res >= 20)
ast_log(LOG_DEBUG, "chan_sip: ast_sched_runq ran %d all at once\n", res);
}
/* Send MWI notifications to peers - static and cached realtime peers */
t = time(NULL);
ASTOBJ_CONTAINER_TRAVERSE(&peerl, !peer, do {
Kevin P. Fleming
committed
if ((curpeernum > lastpeernum) && does_peer_need_mwi(iterator)) {
lastpeernum = curpeernum;
peer = ASTOBJ_REF(iterator);
};
curpeernum++;
} while (0)
Mark Spencer
committed
);
Mark Spencer
committed
if (peer) {
Mark Spencer
committed
sip_send_mwi_to_peer(peer);
Mark Spencer
committed
ASTOBJ_UNREF(peer,sip_destroy_peer);
} else {
/* Reset where we come from */
lastpeernum = -1;
}
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(&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(void *data)
{
struct sip_peer *peer = data;
if (peer->lastms > -1) {
ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE! Last qualify: %d\n", peer->name, peer->lastms);
manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unreachable\r\nTime: %d\r\n", peer->name, -1);
}
if (peer->call)
sip_destroy(peer->call);
peer->call = NULL;
peer->lastms = -1;
ast_device_state_changed("SIP/%s", peer->name);
/* Try again quickly */
peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer);
return 0;
}
/*! \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;
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;
}
if (peer->call > 0) {
Russell Bryant
committed
if (sipdebug)
ast_log(LOG_NOTICE, "Still have a QUALIFY dialog active, deleting\n");
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 */
if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip))
p->ourip = __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));
transmit_invite(p, SIP_INVITE, 0, 2);
transmit_invite(p, SIP_OPTIONS, 0, 2);
#endif
gettimeofday(&peer->ps, NULL);
peer->pokeexpire = ast_sched_add(sched, DEFAULT_MAXMS * 2, sip_poke_noanswer, peer);
return 0;
}
/*! \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
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
If we return AST_DEVICE_UNKNOWN, the device state engine will try to find
out a state by walking the channel list.
Kevin P. Fleming
committed
*/
static int sip_devicestate(void *data)
{
char *host;
char *tmp;
struct hostent *hp;
struct sip_peer *p;
int res = AST_DEVICE_INVALID;
host = ast_strdupa(data);
if ((tmp = strchr(host, '@')))
host = tmp + 1;
if (option_debug > 2)
Kevin P. Fleming
committed
ast_log(LOG_DEBUG, "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 */
/* if qualify is turned on, check the status */
if (p->maxms && (p->lastms > p->maxms)) {
res = AST_DEVICE_UNAVAILABLE;
} else {
/* qualify is not on, or the peer is responding properly */
/* check call limit */
Kevin P. Fleming
committed
if (p->call_limit && (p->inUse == p->call_limit))
Kevin P. Fleming
committed
res = AST_DEVICE_BUSY;
Kevin P. Fleming
committed
else if (p->call_limit && p->inUse)
res = AST_DEVICE_INUSE;
else
res = AST_DEVICE_NOT_INUSE;
if (p->onHold)
res = AST_DEVICE_ONHOLD;
else if (p->inRinging) {
if (p->inRinging == p->inUse)
res = AST_DEVICE_RINGING;
else
res = AST_DEVICE_RINGINUSE;
}
Kevin P. Fleming
committed
}
} else {
/* there is no address, it's unavailable */
res = AST_DEVICE_UNAVAILABLE;
ASTOBJ_UNREF(p,sip_destroy_peer);
} else {
hp = ast_gethostbyname(host, &ahp);
if (hp)
res = AST_DEVICE_UNKNOWN;
}
return res;
}
/*! \brief PBX interface function -build SIP pvt structure */
/* SIP calls initiated by the PBX arrive here */
static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause)
{
int oldformat;
struct sip_pvt *p;
struct ast_channel *tmpc = NULL;
char *ext, *host;
if (!(format &= ((AST_FORMAT_MAX_AUDIO << 1) - 1))) {
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 */
if (option_debug)
ast_log(LOG_DEBUG, "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))) {
Olle Johansson
committed
ast_log(LOG_ERROR, "Unable to build sip pvt data for '%s' (Out of memory or socket error)\n", (char *)data);
*cause = AST_CAUSE_SWITCH_CONGESTION;
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
Kevin P. Fleming
committed
ast_copy_string(tmp, dest, sizeof(tmp));
*host++ = '\0';
Mark Spencer
committed
*cause = AST_CAUSE_UNREGISTERED;
if (option_debug > 2)
ast_log(LOG_DEBUG, "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 */
if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip))
p->ourip = __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_free(p, fullcontact);
printf("Setting up to call extension '%s' at '%s'\n", ext ? ext : "<none>", host);
p->prefcodec = oldformat; /* Format for this call */
Mark Spencer
committed
ast_mutex_lock(&p->lock);
Kevin P. Fleming
committed
tmpc = sip_new(p, AST_STATE_DOWN, host); /* Place the call */
Mark Spencer
committed
ast_mutex_unlock(&p->lock);
\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)
{
int res = 0;
Kevin P. Fleming
committed
static int dep_insecure_very = 0;
static int dep_insecure_yes = 0;
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
res = 1;
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);
res = 1;
} else if (!strcasecmp(v->name, "useclientcode")) {
ast_set_flag(&mask[0], SIP_USECLIENTCODE);
ast_set2_flag(&flags[0], ast_true(v->value), SIP_USECLIENTCODE);
res = 1;
} 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);
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
14774
14775
14776
14777
14778
14779
14780
14781
14782
14783
14784
14785
14786
14787
14788
14789
14790
14791
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_PORT | SIP_INSECURE_INVITE);
ast_clear_flag(&flags[0], SIP_INSECURE_PORT | SIP_INSECURE_INVITE);
Kevin P. Fleming
committed
if (!strcasecmp(v->value, "very")) {
ast_set_flag(&flags[0], SIP_INSECURE_PORT | SIP_INSECURE_INVITE);
Kevin P. Fleming
committed
if (!dep_insecure_very) {
ast_log(LOG_WARNING, "insecure=very at line %d is deprecated; use insecure=port,invite instead\n", v->lineno);
dep_insecure_very = 1;
}
}
else if (ast_true(v->value)) {
ast_set_flag(&flags[0], SIP_INSECURE_PORT);
Kevin P. Fleming
committed
if (!dep_insecure_yes) {
ast_log(LOG_WARNING, "insecure=%s at line %d is deprecated; use insecure=port instead\n", v->value, v->lineno);
dep_insecure_yes = 1;
}
}
else if (!ast_false(v->value)) {
char buf[64];
char *word, *next;
Kevin P. Fleming
committed
ast_copy_string(buf, v->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", 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);
global_allowguest = ast_true(v->value) ? 1 : 0;
} 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, "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);
} 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);
}
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_log(LOG_DEBUG, "Added local SIP domain '%s'\n", domain);
return 1;
}
/*! \brief check_sip_domain: Check if domain part of uri is local to our server */
14891
14892
14893
14894
14895
14896
14897
14898
14899
14900
14901
14902
14903
14904
14905
14906
14907
14908
14909
14910
14911
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)))
free(d);
AST_LIST_UNLOCK(&domain_list);
}
/*! \brief Add realm authentication in list */
static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, char *configuration, int lineno)
{
char authcopy[256];
char *username=NULL, *realm=NULL, *secret=NULL, *md5secret=NULL;
if (ast_strlen_zero(configuration))
ast_log(LOG_DEBUG, "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;
if (option_verbose > 2)
ast_verbose("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;
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;