Newer
Older
append_history(p, "Masq (cont)", "...new owner: %s\n", p->owner->name);
if (p->owner != oldchan)
ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
else {
p->owner = newchan;
ret = 0;
Mark Spencer
committed
ast_mutex_unlock(&p->lock);
/*! \brief Send DTMF character on SIP channel
within one call, we're able to transmit in many methods simultaneously */
static int sip_senddigit(struct ast_channel *ast, char digit)
{
struct sip_pvt *p = ast->tech_pvt;
switch (ast_test_flag(&p->flags[0], SIP_DTMF)) {
break;
case SIP_DTMF_RFC2833:
if (p->rtp)
ast_rtp_senddigit(p->rtp, digit);
break;
case SIP_DTMF_INBAND:
/*! \brief Transfer SIP call */
Mark Spencer
committed
static int sip_transfer(struct ast_channel *ast, const char *dest)
struct sip_pvt *p = ast->tech_pvt;
if (ast->_state == AST_STATE_RING)
res = sip_sipredirect(p, dest);
else
res = transmit_refer(p, dest);
/*! \brief Play indication to user
* With SIP a lot of indications is sent as messages, letting the device play
the indication - busy signal, congestion etc
\return -1 to force ast_indicate to send indication in audio, 0 if SIP can handle the indication by sending a message
*/
Kevin P. Fleming
committed
static int sip_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
struct sip_pvt *p = ast->tech_pvt;
int res = 0;
ast_mutex_lock(&p->lock);
switch(condition) {
case AST_CONTROL_RINGING:
if (!ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) ||
(ast_test_flag(&p->flags[0], SIP_PROG_INBAND) == SIP_PROG_INBAND_NEVER)) {
/* Send 180 ringing if out-of-band seems reasonable */
transmit_response(p, "180 Ringing", &p->initreq);
ast_set_flag(&p->flags[0], SIP_RINGING);
if (ast_test_flag(&p->flags[0], SIP_PROG_INBAND) != SIP_PROG_INBAND_YES)
/* Well, if it's not reasonable, just send in-band */
transmit_response(p, "486 Busy Here", &p->initreq);
ast_set_flag(&p->flags[0], SIP_ALREADYGONE);
ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
transmit_response(p, "503 Service Unavailable", &p->initreq);
ast_set_flag(&p->flags[0], SIP_ALREADYGONE);
ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
if ((ast->_state != AST_STATE_UP) &&
!ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
!ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
Kevin P. Fleming
committed
transmit_response(p, "100 Trying", &p->initreq);
break;
}
res = -1;
break;
case AST_CONTROL_PROGRESS:
if ((ast->_state != AST_STATE_UP) &&
!ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
!ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE);
ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
case AST_CONTROL_HOLD: /* The other part of the bridge are put on hold */
Russell Bryant
committed
if (sipdebug)
Olle Johansson
committed
ast_log(LOG_DEBUG, "Bridged channel now on hold - %s\n", p->callid);
res = -1;
break;
case AST_CONTROL_UNHOLD: /* The other part of the bridge are back from hold */
Russell Bryant
committed
if (sipdebug)
ast_log(LOG_DEBUG, "Bridged channel is back from hold, let's talk! : %s\n", p->callid);
res = -1;
break;
case AST_CONTROL_VIDUPDATE: /* Request a video frame update */
if (p->vrtp && !ast_test_flag(&p->flags[0], SIP_NOVIDEO)) {
ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", condition);
/*! \brief Initiate a call in the SIP channel
called from sip_request_call (calls from the pbx ) */
static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *title)
int what;
Mark Spencer
committed
ast_mutex_unlock(&i->lock);
/* Don't hold a sip pvt lock while we allocate a channel */
Mark Spencer
committed
ast_mutex_lock(&i->lock);
if (!tmp) {
ast_log(LOG_WARNING, "Unable to allocate SIP channel structure\n");
return NULL;
}
tmp->tech = &sip_tech;
/* Select our native format based on codec preference until we receive
something from another device to the contrary. */
if (i->jointcapability)
what = i->jointcapability;
what = i->capability;
what = global_capability;
tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK);
fmt = ast_best_codec(tmp->nativeformats);
if (title)
Tilghman Lesher
committed
ast_string_field_build(tmp, name, "SIP/%s-%04lx", title, ast_random() & 0xffff);
else if (strchr(i->fromdomain,':'))
ast_string_field_build(tmp, name, "SIP/%s-%08x", strchr(i->fromdomain,':')+1, (int)(long)(i));
ast_string_field_build(tmp, name, "SIP/%s-%08x", i->fromdomain, (int)(long)(i));
if (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) {
i->vad = ast_dsp_new();
ast_dsp_set_features(i->vad, DSP_FEATURE_DTMF_DETECT);
ast_dsp_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
}
if (i->rtp) {
tmp->fds[0] = ast_rtp_fd(i->rtp);
tmp->fds[1] = ast_rtcp_fd(i->rtp);
}
if (i->vrtp) {
tmp->fds[2] = ast_rtp_fd(i->vrtp);
tmp->fds[3] = ast_rtcp_fd(i->vrtp);
}
if (state == AST_STATE_RING)
tmp->rings = 1;
tmp->adsicpe = AST_ADSI_UNAVAILABLE;
tmp->writeformat = fmt;
tmp->rawwriteformat = fmt;
tmp->readformat = fmt;
tmp->rawreadformat = fmt;
tmp->tech_pvt = i;
tmp->callgroup = i->callgroup;
tmp->pickupgroup = i->pickupgroup;
tmp->cid.cid_pres = i->callingpres;
if (!ast_strlen_zero(i->accountcode))
ast_string_field_set(tmp, accountcode, i->accountcode);
if (i->amaflags)
tmp->amaflags = i->amaflags;
if (!ast_strlen_zero(i->language))
ast_string_field_set(tmp, language, i->language);
if (!ast_strlen_zero(i->musicclass))
ast_string_field_set(tmp, musicclass, i->musicclass);
i->owner = tmp;
ast_mutex_lock(&usecnt_lock);
usecnt++;
ast_mutex_unlock(&usecnt_lock);
ast_copy_string(tmp->context, i->context, sizeof(tmp->context));
ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten));
if (!ast_strlen_zero(i->cid_num))
tmp->cid.cid_num = ast_strdup(i->cid_num);
if (!ast_strlen_zero(i->cid_name))
tmp->cid.cid_name = ast_strdup(i->cid_name);
tmp->cid.cid_rdnis = ast_strdup(i->rdnis);
if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s"))
tmp->cid.cid_dnid = ast_strdup(i->exten);
if (!ast_strlen_zero(i->uri))
pbx_builtin_setvar_helper(tmp, "SIPURI", i->uri);
if (!ast_strlen_zero(i->domain))
pbx_builtin_setvar_helper(tmp, "SIPDOMAIN", i->domain);
if (!ast_strlen_zero(i->useragent))
pbx_builtin_setvar_helper(tmp, "SIPUSERAGENT", i->useragent);
if (!ast_strlen_zero(i->callid))
pbx_builtin_setvar_helper(tmp, "SIPCALLID", i->callid);
ast_setstate(tmp, state);
if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) {
ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
tmp->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
ast_hangup(tmp);
tmp = NULL;
}
/* Set channel variables for this call from configuration */
for (v = i->chanvars ; v ; v = v->next)
pbx_builtin_setvar_helper(tmp,v->name,v->value);
if (recordhistory)
append_history(i, "NewChan", "Channel %s - from %s", tmp->name, i->callid);
/*! \brief Reads one line of SIP message body */
Olle Johansson
committed
static const char *get_sdp_by_line(const char* line, const char *name, int nameLen)
if (strncasecmp(line, name, nameLen) == 0 && line[nameLen] == '=')
return ast_skip_blanks(line + nameLen + 1);
/*! \brief get_sdp_iterate: lookup 'name' in the request starting
* at the 'start' line. Returns the matching line, and 'start'
* is updated with the next line number.
*/
Olle Johansson
committed
static const char* get_sdp_iterate(int* start, struct sip_request *req, const char *name)
{
int len = strlen(name);
while (*start < req->lines) {
const char *r = get_sdp_by_line(req->line[(*start)++], name, len);
if (r[0] != '\0')
return r;
}
return "";
/*! \brief get_sdp: Gets all kind of SIP message bodies, including SDP,
but the name wrongly applies _only_ sdp */
static const char *get_sdp(struct sip_request *req, const char *name)
{
int dummy = 0;
return get_sdp_iterate(&dummy, req, name);
}
Olle Johansson
committed
/*! \brief Find compressed SIP alias */
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
static const char *find_alias(const char *name, const char *_default)
{
/*! \brief Structure for conversion between compressed SIP and "normal" SIP */
static const struct cfalias {
char * const fullname;
char * const shortname;
} aliases[] = {
{ "Content-Type", "c" },
{ "Content-Encoding", "e" },
{ "From", "f" },
{ "Call-ID", "i" },
{ "Contact", "m" },
{ "Content-Length", "l" },
{ "Subject", "s" },
{ "To", "t" },
{ "Supported", "k" },
{ "Refer-To", "r" },
{ "Referred-By", "b" },
{ "Allow-Events", "u" },
{ "Event", "o" },
{ "Via", "v" },
{ "Accept-Contact", "a" },
{ "Reject-Contact", "j" },
{ "Request-Disposition", "d" },
{ "Session-Expires", "x" },
};
Olle Johansson
committed
for (x=0; x<sizeof(aliases) / sizeof(aliases[0]); x++)
if (!strcasecmp(aliases[x].fullname, name))
return aliases[x].shortname;
return _default;
}
static const char *__get_header(const struct sip_request *req, const char *name, int *start)
{
int pass;
/*
* Technically you can place arbitrary whitespace both before and after the ':' in
* a header, although RFC3261 clearly says you shouldn't before, and place just
* one afterwards. If you shouldn't do it, what absolute idiot decided it was
* a good idea to say you can do it, and if you can do it, why in the hell would.
* you say you shouldn't.
* Anyways, pedanticsipchecking controls whether we allow spaces before ':',
* and we always allow spaces after that for compatibility.
*/
for (pass = 0; name && pass < 2;pass++) {
int x, len = strlen(name);
for (x=*start; x<req->headers; x++) {
if (!strncasecmp(req->header[x], name, len)) {
char *r = req->header[x] + len; /* skip name */
if (pedanticsipchecking)
r = ast_skip_blanks(r);
if (*r == ':') {
return ast_skip_blanks(r+1);
}
}
}
if (pass == 0) /* Try aliases */
name = find_alias(name, NULL);
}
/* Don't return NULL, so get_header is always a valid pointer */
return "";
}
/*! \brief Get header from SIP request */
static const char *get_header(const struct sip_request *req, const char *name)
{
int start = 0;
return __get_header(req, name, &start);
}
/*! \brief Read RTP from network */
static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p)
/* Retrieve audio/etc from channel. Assumes p->lock is already held. */
struct ast_frame *f;
if (!p->rtp) {
/* We have no RTP allocated for this channel */
return &ast_null_frame;
}
switch(ast->fdno) {
case 0:
f = ast_rtp_read(p->rtp); /* RTP Audio */
break;
case 1:
f = ast_rtcp_read(p->rtp); /* RTCP Control Channel */
break;
case 2:
f = ast_rtp_read(p->vrtp); /* RTP Video */
break;
case 3:
f = ast_rtcp_read(p->vrtp); /* RTCP Control Channel for video */
break;
default:
f = &ast_null_frame;
/* Don't forward RFC2833 if we're not supposed to */
if (f && (f->frametype == AST_FRAME_DTMF) &&
(ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_RFC2833))
return &ast_null_frame;
/* We already hold the channel lock */
if (f->frametype == AST_FRAME_VOICE) {
if (f->subclass != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) {
if (option_debug)
ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass);
p->owner->nativeformats = (p->owner->nativeformats & AST_FORMAT_VIDEO_MASK) | f->subclass;
Mark Spencer
committed
ast_set_read_format(p->owner, p->owner->readformat);
ast_set_write_format(p->owner, p->owner->writeformat);
if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) {
f = ast_dsp_process(p->owner, p->vad, f);
if (option_debug && f && (f->frametype == AST_FRAME_DTMF))
ast_log(LOG_DEBUG, "* Detected inband DTMF '%c'\n", f->subclass);
}
/*! \brief Read SIP RTP from channel */
static struct ast_frame *sip_read(struct ast_channel *ast)
{
struct ast_frame *fr;
struct sip_pvt *p = ast->tech_pvt;
ast_mutex_lock(&p->lock);
fr = sip_rtp_read(ast, p);
time(&p->lastrtprx);
ast_mutex_unlock(&p->lock);
Olle Johansson
committed
/*! \brief Generate 32 byte random string for callid's etc */
static char *generate_random_string(char *buf, size_t size)
Tilghman Lesher
committed
long val[4];
for (x=0; x<4; x++)
Tilghman Lesher
committed
val[x] = ast_random();
snprintf(buf, size, "%08lx%08lx%08lx%08lx", val[0], val[1], val[2], val[3]);
Olle Johansson
committed
return buf;
}
/*! \brief Build SIP Call-ID value for a non-REGISTER transaction */
static void build_callid_pvt(struct sip_pvt *pvt)
{
char iabuf[INET_ADDRSTRLEN];
char buf[33];
const char *host = S_OR(pvt->fromdomain, ast_inet_ntoa(iabuf, sizeof(iabuf), pvt->ourip));
Olle Johansson
committed
ast_string_field_build(pvt, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host);
}
/*! \brief Build SIP Call-ID value for a REGISTER transaction */
static void build_callid_registry(struct sip_registry *reg, struct in_addr ourip, const char *fromdomain)
{
char iabuf[INET_ADDRSTRLEN];
Olle Johansson
committed
char buf[33];
const char *host = S_OR(fromdomain, ast_inet_ntoa(iabuf, sizeof(iabuf), ourip));
Olle Johansson
committed
ast_string_field_build(reg, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host);
/*! \brief Make our SIP dialog tag */
Kevin P. Fleming
committed
static void make_our_tag(char *tagbuf, size_t len)
{
Tilghman Lesher
committed
snprintf(tagbuf, len, "as%08lx", ast_random());
Kevin P. Fleming
committed
}
/*! \brief Allocate SIP_PVT structure and set defaults */
static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *sin,
int useglobal_nat, const int intended_method)
if (!(p = ast_calloc(1, sizeof(*p))))
Kevin P. Fleming
committed
if (ast_string_field_init(p, 512)) {
free(p);
return NULL;
}
ast_mutex_init(&p->lock);
Kevin P. Fleming
committed
p->method = intended_method;
Kevin P. Fleming
committed
p->subscribed = NONE;
p->stateid = -1;
Olle Johansson
committed
p->prefs = default_prefs; /* Set default codecs for this call */
if (intended_method != SIP_OPTIONS) /* Peerpoke has it's own system */
p->timer_t1 = 500; /* Default SIP retransmission timer T1 (RFC 3261) */
p->sa = *sin;
if (ast_sip_ouraddrfor(&p->sa.sin_addr,&p->ourip))
p->ourip = __ourip;
p->ourip = __ourip;
ast_copy_flags(&p->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY);
ast_copy_flags(&p->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
Kevin P. Fleming
committed
Tilghman Lesher
committed
p->branch = ast_random();
Kevin P. Fleming
committed
make_our_tag(p->tag, sizeof(p->tag));
p->ocseq = INITIAL_CSEQ;
Kevin P. Fleming
committed
if (sip_methods[intended_method].need_rtp) {
p->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
if (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT))
Kevin P. Fleming
committed
p->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
if (!p->rtp || (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && !p->vrtp)) {
ast_log(LOG_WARNING, "Unable to create RTP audio %s session: %s\n",
ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "and video" : "", strerror(errno));
Kevin P. Fleming
committed
ast_mutex_destroy(&p->lock);
Kevin P. Fleming
committed
ast_variables_destroy(p->chanvars);
p->chanvars = NULL;
}
free(p);
return NULL;
ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_INFO);
Kevin P. Fleming
committed
ast_rtp_settos(p->rtp, global_tos_audio);
Kevin P. Fleming
committed
ast_rtp_settos(p->vrtp, global_tos_video);
ast_rtp_setdtmf(p->vrtp, 0);
}
Kevin P. Fleming
committed
p->rtptimeout = global_rtptimeout;
p->rtpholdtimeout = global_rtpholdtimeout;
p->rtpkeepalive = global_rtpkeepalive;
Kevin P. Fleming
committed
Mark Spencer
committed
if (useglobal_nat && sin) {
int natflags;
/* Setup NAT structure according to global settings if we have an address */
ast_copy_flags(&p->flags[0], &global_flags[0], SIP_NAT);
p->recv = *sin;
natflags = ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE;
Kevin P. Fleming
committed
if (p->rtp)
ast_rtp_setnat(p->rtp, natflags);
if (p->vrtp)
ast_rtp_setnat(p->vrtp, natflags);
Kevin P. Fleming
committed
if (p->method != SIP_REGISTER)
ast_string_field_set(p, fromdomain, default_fromdomain);
build_via(p);
build_callid_pvt(p);
ast_string_field_set(p, callid, callid);
ast_string_field_set(p, musicclass, default_musicclass);
Mark Spencer
committed
p->capability = global_capability;
p->allowtransfer = global_allowtransfer;
if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
(ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
ast_string_field_set(p, context, default_context);
Kevin P. Fleming
committed
/* Add to active dialog list */
ast_mutex_lock(&iflock);
ast_mutex_unlock(&iflock);
Kevin P. Fleming
committed
ast_log(LOG_DEBUG, "Allocating new SIP dialog for %s - %s (%s)\n", callid ? callid : "(No Call-ID)", sip_methods[intended_method].text, p->rtp ? "With RTP" : "No RTP");
/*! \brief Connect incoming SIP message to current dialog or create new dialog structure
Called by handle_request, sipsock_read */
Kevin P. Fleming
committed
static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin, const int intended_method)
char *tag = ""; /* note, tag is never NULL */
char totag[128];
char fromtag[128];
const char *callid = get_header(req, "Call-ID");
Olle Johansson
committed
const char *from = get_header(req, "From");
const char *to = get_header(req, "To");
const char *cseq = get_header(req, "Cseq");
if (!callid || !to || !from || !cseq) /* Call-ID, to, from and Cseq are required by RFC 3261. (Max-forwards and via too - ignored now) */
return NULL; /* Invalid packet */
/* In principle Call-ID's uniquely identify a call, but with a forking SIP proxy
we need more to identify a branch - so we have to check branch, from
and to tags to identify a call leg.
For Asterisk to behave correctly, you need to turn on pedanticsipchecking
in sip.conf
*/
if (gettag(req, "To", totag, sizeof(totag)))
ast_set_flag(req, SIP_PKT_WITH_TOTAG); /* Used in handle_request/response */
gettag(req, "From", fromtag, sizeof(fromtag));
tag = (req->method == SIP_RESPONSE) ? totag : fromtag;
if (option_debug > 4 )
ast_log(LOG_DEBUG, "= Looking for Call ID: %s (Checking %s) --From tag %s --To-tag %s \n", callid, req->method==SIP_RESPONSE ? "To" : "From", fromtag, totag);
ast_mutex_lock(&iflock);
for (p = iflist; p; p = p->next) {
/* In pedantic, we do not want packets with bad syntax to be connected to a PVT */
if (req->method == SIP_REGISTER)
found = (!strcmp(p->callid, callid));
else
found = (!strcmp(p->callid, callid) &&
(!pedanticsipchecking || !tag || ast_strlen_zero(p->theirtag) || !strcmp(p->theirtag, tag))) ;
if (option_debug > 4)
ast_log(LOG_DEBUG, "= %s Their Call ID: %s Their Tag %s Our tag: %s\n", found ? "Found" : "No match", p->callid, p->theirtag, p->tag);
/* If we get a new request within an existing to-tag - check the to tag as well */
if (pedanticsipchecking && found && req->method != SIP_RESPONSE) { /* SIP Request */
if (p->tag[0] == '\0' && totag[0]) {
/* We have no to tag, but they have. Wrong dialog */
} else if (totag[0]) { /* Both have tags, compare them */
if (strcmp(totag, p->tag)) {
found = FALSE; /* This is not our packet */
}
}
if (!found && option_debug > 4)
ast_log(LOG_DEBUG, "= Being pedantic: This is not our match on request: Call ID: %s Ourtag <null> Totag %s Method %s\n", p->callid, totag, sip_methods[req->method].text);
}
if (found) {
ast_mutex_lock(&p->lock);
ast_mutex_unlock(&iflock);
ast_mutex_unlock(&iflock);
Olle Johansson
committed
/* Allocate new call */
if ((p = sip_alloc(callid, sin, 1, intended_method)))
ast_mutex_lock(&p->lock);
/*! \brief Parse register=> line in sip.conf and add to registry */
static int sip_register(char *value, int lineno)
{
struct sip_registry *reg;
char *username=NULL, *hostname=NULL, *secret=NULL, *authuser=NULL;
char *porta=NULL;
char *contact=NULL;
char *stringp=NULL;
if (!value)
return -1;
Kevin P. Fleming
committed
ast_copy_string(copy, value, sizeof(copy));
username = stringp;
hostname = strrchr(stringp, '@');
if (hostname)
*hostname++ = '\0';
if (ast_strlen_zero(username) || ast_strlen_zero(hostname)) {
ast_log(LOG_WARNING, "Format for registration is user[:secret[:authuser]]@host[:port][/contact] at line %d\n", lineno);
stringp = username;
if (username) {
secret = strsep(&stringp, ":");
if (secret)
authuser = strsep(&stringp, ":");
}
stringp = hostname;
hostname = strsep(&stringp, "/");
if (hostname)
contact = strsep(&stringp, "/");
if (ast_strlen_zero(contact))
stringp=hostname;
hostname = strsep(&stringp, ":");
if (porta && !atoi(porta)) {
ast_log(LOG_WARNING, "%s is not a valid port number at line %d\n", porta, lineno);
return -1;
}
if (!(reg = ast_calloc(1, sizeof(*reg)))) {
ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry entry\n");
if (ast_string_field_init(reg, 256)) {
ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry strings\n");
free(reg);
return -1;
}
regobjs++;
ASTOBJ_INIT(reg);
ast_string_field_set(reg, contact, contact);
ast_string_field_set(reg, username, username);
ast_string_field_set(reg, hostname, hostname);
ast_string_field_set(reg, authuser, authuser);
ast_string_field_set(reg, secret, secret);
reg->expire = -1;
reg->timeout = -1;
reg->refresh = default_expiry;
reg->portno = porta ? atoi(porta) : 0;
reg->ocseq = INITIAL_CSEQ;
ASTOBJ_CONTAINER_LINK(®l, reg); /* Add the new registry entry to the list */
ASTOBJ_UNREF(reg,sip_registry_destroy);
/*! \brief Parse multiline SIP headers into one header
This is enabled if pedanticsipchecking is enabled */
static int lws2sws(char *msgbuf, int len)
int h = 0, t = 0;
int lws = 0;
for (; h < len;) {
/* Eliminate all CRs */
if (msgbuf[h] == '\r') {
h++;
continue;
}
/* Check for end-of-line */
if (msgbuf[h] == '\n') {
if (h + 1 == len)
break;
/* Check for a continuation line */
if (msgbuf[h + 1] == ' ' || msgbuf[h + 1] == '\t') {
/* Merge continuation line */
h++;
continue;
}
/* Propagate LF and start new line */
msgbuf[t++] = msgbuf[h++];
lws = 0;
continue;
}
if (msgbuf[h] == ' ' || msgbuf[h] == '\t') {
if (lws) {
h++;
continue;
}
msgbuf[t++] = msgbuf[h++];
lws = 1;
continue;
}
msgbuf[t++] = msgbuf[h++];
}
msgbuf[t] = '\0';
return t;
}
Olle Johansson
committed
/*! \brief Parse a SIP message
\note this function is used both on incoming and outgoing packets
*/
static void parse_request(struct sip_request *req)
c = req->data;
/* First header starts immediately */
req->header[f] = c;
while(*c) {
if (*c == '\n') {
/* We've got a new header */
*c = 0;
Russell Bryant
committed
if (sipdebug && option_debug > 3)
ast_log(LOG_DEBUG, "Header %d: %s (%d)\n", f, req->header[f], (int) strlen(req->header[f]));
if (ast_strlen_zero(req->header[f])) {
/* Line by itself means we're now in content */
c++;
break;
}
if (f >= SIP_MAX_HEADERS - 1) {
ast_log(LOG_WARNING, "Too many SIP headers. Ignoring.\n");
} else
f++;
req->header[f] = c + 1;
} else if (*c == '\r') {
/* Ignore but eliminate \r's */
*c = 0;
if (!ast_strlen_zero(req->header[f])) {
Russell Bryant
committed
if (sipdebug && option_debug > 3)
ast_log(LOG_DEBUG, "Header %d: %s (%d)\n", f, req->header[f], (int) strlen(req->header[f]));
}
req->headers = f;
/* Now we process any mime content */
f = 0;
req->line[f] = c;
while(*c) {
if (*c == '\n') {
/* We've got a new line */
*c = 0;
Russell Bryant
committed
if (sipdebug && option_debug > 3)
ast_log(LOG_DEBUG, "Line: %s (%d)\n", req->line[f], (int) strlen(req->line[f]));
ast_log(LOG_WARNING, "Too many SDP lines. Ignoring.\n");
} else
f++;
req->line[f] = c + 1;
} else if (*c == '\r') {
/* Ignore and eliminate \r's */
*c = 0;
}
c++;
}
/* Check for last line */
if (!ast_strlen_zero(req->line[f]))
f++;
req->lines = f;
if (*c)
ast_log(LOG_WARNING, "Odd content, extra stuff left over ('%s')\n", c);
/* Split up the first line parts */
determine_firstline_parts(req);
/*! \brief Process SIP SDP and activate RTP channels*/
static int process_sdp(struct sip_pvt *p, struct sip_request *req)
{
const char *m;
const char *c;
const char *a;
char iabuf[INET_ADDRSTRLEN];
int portno = -1;
int vportno = -1;
int vpeercapability=0, vpeernoncodeccapability=0;
int destiterator = 0;
int sendonly = 0;
int x,y;
struct ast_channel *bridgepeer = NULL;
if (!p->rtp) {
ast_log(LOG_ERROR, "Got SDP but have no RTP session allocated.\n");
return -1;
}
/* Update our last rtprx when we receive an SDP, too */
time(&p->lastrtprx);
/* Get codec and RTP info from SDP */
if (strcasecmp(get_header(req, "Content-Type"), "application/sdp")) {
ast_log(LOG_NOTICE, "Content is '%s', not 'application/sdp'\n", get_header(req, "Content-Type"));
return -1;
}
m = get_sdp(req, "m");
destiterator = 0;
c = get_sdp_iterate(&destiterator, req, "c");
if (ast_strlen_zero(m) || ast_strlen_zero(c)) {
ast_log(LOG_WARNING, "Insufficient information for SDP (m = '%s', c = '%s')\n", m, c);
return -1;
}
if (sscanf(c, "IN IP4 %256s", host) != 1) {
ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c);
return -1;
}
/* XXX This could block for a long time, and block the main thread! XXX */
hp = ast_gethostbyname(host, &ahp);
if (!hp) {
ast_log(LOG_WARNING, "Unable to lookup host in c= line, '%s'\n", c);
return -1;
}
iterator = 0;
ast_set_flag(&p->flags[0], SIP_NOVIDEO);
while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') {
int found = 0;
if ((sscanf(m, "audio %d/%d RTP/AVP %n", &x, &y, &len) == 2) ||
(sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1)) {
found = 1;
portno = x;
/* Scan through the RTP payload types specified in a "m=" line: */
ast_rtp_pt_clear(p->rtp);
for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
return -1;
}
ast_verbose("Found RTP audio format %d\n", codec);
ast_rtp_set_m_type(p->rtp, codec);
}
}
ast_rtp_pt_clear(p->vrtp); /* Must be cleared in case no m=video line exists */
if (p->vrtp && (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1)) {
found = 1;
ast_clear_flag(&p->flags[0], SIP_NOVIDEO);
vportno = x;
/* Scan through the RTP payload types specified in a "m=" line: */
for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
return -1;
}
ast_verbose("Found RTP video format %d\n", codec);
ast_rtp_set_m_type(p->vrtp, codec);
}
}
if (!found )
ast_log(LOG_WARNING, "Unknown SDP media type in offer: %s\n", m);
}
if (portno == -1 && vportno == -1) {
/* No acceptable offer found in SDP */
return -2;
/* Check for Media-description-level-address for audio */
if (pedanticsipchecking) {
c = get_sdp_iterate(&destiterator, req, "c");
if (!ast_strlen_zero(c)) {
if (sscanf(c, "IN IP4 %256s", host) != 1) {
ast_log(LOG_WARNING, "Invalid secondary host in c= line, '%s'\n", c);
} else {
/* XXX This could block for a long time, and block the main thread! XXX */
hp = ast_gethostbyname(host, &ahp);
if (!hp) {
ast_log(LOG_WARNING, "Unable to lookup host in secondary c= line, '%s'\n", c);
}
}
}
}
/* RTP addresses and ports for audio and video */
sin.sin_family = AF_INET;
memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
/* Setup audio port number */
if (p->rtp && sin.sin_port) {
if (debug) {
ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(iabuf,sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port));
}
}
/* Check for Media-description-level-address for video */
if (pedanticsipchecking) {
c = get_sdp_iterate(&destiterator, req, "c");
if (!ast_strlen_zero(c)) {
if (sscanf(c, "IN IP4 %256s", host) != 1) {
ast_log(LOG_WARNING, "Invalid secondary host in c= line, '%s'\n", c);
} else {
/* XXX This could block for a long time, and block the main thread! XXX */
hp = ast_gethostbyname(host, &ahp);
if (!hp) {
ast_log(LOG_WARNING, "Unable to lookup host in secondary c= line, '%s'\n", c);
}
}
}
}
/* Setup video port number */
sin.sin_port = htons(vportno);
if (p->vrtp && sin.sin_port) {
ast_rtp_set_peer(p->vrtp, &sin);
if (debug) {
ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(iabuf,sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port));
}
}
/* Next, scan through each "a=rtpmap:" line, noting each