Newer
Older
return send_request(p, &req, 1, p->ocseq);
/*! \brief Send SIP INFO with video update request */
static int transmit_info_with_vidupdate(struct sip_pvt *p)
{
struct sip_request req;
reqprep(&req, p, SIP_INFO, 0, 1);
add_vidupdate(&req);
return send_request(p, &req, 1, p->ocseq);
}
/*! \brief Transmit generic SIP request */
static int transmit_request(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch)
reqprep(&resp, p, sipmethod, seqno, newbranch);
add_header_contentLength(&resp, 0);
return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq);
/*! \brief Transmit SIP request, auth added */
static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch)
{
struct sip_request resp;
reqprep(&resp, p, sipmethod, seqno, newbranch);
if (!ast_strlen_zero(p->realm)) {
Mark Spencer
committed
memset(digest, 0, sizeof(digest));
if(!build_reply_digest(p, sipmethod, digest, sizeof(digest))) {
if (p->options && p->options->auth_type == PROXY_AUTH)
add_header(&resp, "Proxy-Authorization", digest);
else if (p->options && p->options->auth_type == WWW_AUTH)
add_header(&resp, "Authorization", digest);
else /* Default, to be backwards compatible (maybe being too careful, but leaving it for now) */
add_header(&resp, "Proxy-Authorization", digest);
} else
Kevin P. Fleming
committed
ast_log(LOG_WARNING, "No authentication available for call %s\n", p->callid);
Kevin P. Fleming
committed
/* If we are hanging up and know a cause for that, send it in clear text to make
debugging easier. */
if (sipmethod == SIP_BYE && p->owner && p->owner->hangupcause) {
char buf[10];
add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->owner->hangupcause));
snprintf(buf, sizeof(buf), "%d", p->owner->hangupcause);
add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
Kevin P. Fleming
committed
}
add_header_contentLength(&resp, 0);
return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq);
/*! \brief Remove registration data from realtime database and AST/DB when registration expires */
Mark Spencer
committed
static void destroy_association(struct sip_peer *peer)
{
if (!ast_test_flag(&global_flags[1], SIP_PAGE2_IGNOREREGEXPIRE)) {
if (ast_test_flag(&peer->flags[1], SIP_PAGE2_RT_FROMCONTACT)) {
Mark Spencer
committed
ast_update_realtime("sippeers", "name", peer->name, "fullcontact", "", "ipaddr", "", "port", "", "regseconds", "0", "username", "", NULL);
} else {
ast_db_del("SIP/Registry", peer->name);
}
}
}
/*! \brief Expire registration of SIP peer */
struct sip_peer *peer = data;
if (!peer) /* Hmmm. We have no peer. Weird. */
return 0;
memset(&peer->addr, 0, sizeof(peer->addr));
Mark Spencer
committed
destroy_association(peer); /* remove registration data from storage */
Mark Spencer
committed
manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
register_peer_exten(peer, FALSE); /* Remove regexten */
peer->expire = -1;
ast_device_state_changed("SIP/%s", peer->name);
/* Do we need to release this peer from memory?
Only for realtime peers and autocreated peers
*/
if (ast_test_flag(&peer->flags[1], SIP_PAGE2_SELFDESTRUCT) ||
ast_test_flag(&peer->flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
peer = ASTOBJ_CONTAINER_UNLINK(&peerl, peer); /* Remove from peer list */
ASTOBJ_UNREF(peer, sip_destroy_peer); /* Remove from memory */
/*! \brief Poke peer (send qualify to check if peer is alive and well) */
static int sip_poke_peer_s(void *data)
{
struct sip_peer *peer = data;
peer->pokeexpire = -1;
sip_poke_peer(peer);
return 0;
}
/*! \brief Get registration details from Asterisk DB */
static void reg_source_db(struct sip_peer *peer)
char iabuf[INET_ADDRSTRLEN];
struct in_addr in;
int expiry;
int port;
char *scan, *addr, *port_str, *expiry_str, *username, *contact;
if (ast_test_flag(&peer->flags[1], SIP_PAGE2_RT_FROMCONTACT))
Mark Spencer
committed
return;
if (ast_db_get("SIP/Registry", peer->name, data, sizeof(data)))
return;
scan = data;
addr = strsep(&scan, ":");
port_str = strsep(&scan, ":");
expiry_str = strsep(&scan, ":");
username = strsep(&scan, ":");
contact = scan; /* Contact include sip: and has to be the last part of the database entry as long as we use : as a separator */
if (!inet_aton(addr, &in))
return;
if (port_str)
port = atoi(port_str);
else
return;
if (expiry_str)
expiry = atoi(expiry_str);
else
return;
if (username)
Kevin P. Fleming
committed
ast_copy_string(peer->username, username, sizeof(peer->username));
Kevin P. Fleming
committed
ast_copy_string(peer->fullcontact, contact, sizeof(peer->fullcontact));
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "SIP Seeding peer from astdb: '%s' at %s@%s:%d for %d\n",
peer->name, peer->username, ast_inet_ntoa(iabuf, sizeof(iabuf), in), port, expiry);
memset(&peer->addr, 0, sizeof(peer->addr));
peer->addr.sin_family = AF_INET;
peer->addr.sin_addr = in;
peer->addr.sin_port = htons(port);
if (sipsock < 0) {
/* SIP isn't up yet, so schedule a poke only, pretty soon */
if (peer->pokeexpire > -1)
ast_sched_del(sched, peer->pokeexpire);
Tilghman Lesher
committed
peer->pokeexpire = ast_sched_add(sched, ast_random() % 5000 + 1, sip_poke_peer_s, peer);
} else
sip_poke_peer(peer);
if (peer->expire > -1)
ast_sched_del(sched, peer->expire);
peer->expire = ast_sched_add(sched, (expiry + 10) * 1000, expire_register, peer);
Olle Johansson
committed
register_peer_exten(peer, TRUE);
/*! \brief Save contact header for 200 OK on INVITE */
static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req)
{
char contact[250];
char *c;
Kevin P. Fleming
committed
ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact));
c = get_in_brackets(contact);
/* Save full contact to call pvt for later bye or re-invite */
ast_string_field_set(pvt, fullcontact, c);
/* Save URI for later ACKs, BYE or RE-invites */
ast_string_field_set(pvt, okcontacturi, c);
6184
6185
6186
6187
6188
6189
6190
6191
6192
6193
6194
6195
6196
6197
6198
6199
6200
6201
6202
6203
6204
6205
6206
6207
6208
6209
6210
/* We should return false for URI:s we can't handle,
like sips:, tel:, mailto:,ldap: etc */
return TRUE;
}
/*! \brief Change the other partys IP address based on given contact */
static int set_address_from_contact(struct sip_pvt *pvt)
{
struct hostent *hp;
struct ast_hostent ahp;
int port;
char *c, *host, *pt;
char *contact;
if (ast_test_flag(&pvt->flags[0], SIP_NAT_ROUTE)) {
/* NAT: Don't trust the contact field. Just use what they came to us
with. */
pvt->sa = pvt->recv;
return 0;
}
/* Work on a copy */
contact = ast_strdupa(pvt->fullcontact);
/* XXX this code is repeated all over */
if (strncasecmp(contact, "sip:", 4)) {
ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", contact);
contact += 4;
/* XXX this code is replicated also shortly below */
host = strchr(contact, ';');
if (host)
*host = '\0';
host = strchr(contact, '@');
if (!host) { /* No username part */
host = contact;
*host++ = '\0';
pt = strchr(host, ':');
*pt++ = '\0';
port = atoi(pt);
} else
port = DEFAULT_SIP_PORT;
/* XXX This could block for a long time XXX */
/* We should only do this if it's a name, not an IP */
hp = ast_gethostbyname(host, &ahp);
if (!hp) {
ast_log(LOG_WARNING, "Invalid host name in Contact: (can't resolve in DNS) : '%s'\n", host);
return -1;
pvt->sa.sin_family = AF_INET;
memcpy(&pvt->sa.sin_addr, hp->h_addr, sizeof(pvt->sa.sin_addr));
pvt->sa.sin_port = htons(port);
enum parse_register_result {
PARSE_REGISTER_FAILED,
PARSE_REGISTER_UPDATE,
PARSE_REGISTER_QUERY,
};
/*! \brief Parse contact header and save registration */
static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_request *req)
char contact[BUFSIZ];
char data[BUFSIZ];
char iabuf[INET_ADDRSTRLEN];
const char *expires = get_header(req, "Expires");
const char *useragent;
if (ast_strlen_zero(expires)) { /* No expires header */
expires = strcasestr(get_header(req, "Contact"), ";expires=");
char *ptr;
if ((ptr = strchr(expires, ';')))
*ptr = '\0';
if (sscanf(expires + 9, "%d", &expiry) != 1)
expiry = default_expiry;
} else {
/* Nothing has been specified */
expiry = default_expiry;
}
Kevin P. Fleming
committed
ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact));
if (strchr(contact, '<') == NULL) { /* No <, check for ; and strip it */
char *ptr = strchr(contact, ';'); /* This is Header options, not URI options */
if (ptr)
*ptr = '\0';
}
c = get_in_brackets(contact);
/* if they did not specify Contact: or Expires:, they are querying
what we currently have stored as their contact address, so return
it
*/
if (ast_strlen_zero(c) && ast_strlen_zero(expires)) {
/* If we have an active registration, tell them when the registration is going to expire */
if (p->expire > -1 && !ast_strlen_zero(p->fullcontact))
pvt->expiry = ast_sched_when(sched, p->expire);
} else if (!strcasecmp(c, "*") || !expiry) { /* Unregister this peer */
/* This means remove all registrations and return OK */
memset(&p->addr, 0, sizeof(p->addr));
if (p->expire > -1)
ast_sched_del(sched, p->expire);
p->expire = -1;
Mark Spencer
committed
destroy_association(p);
register_peer_exten(p, 0);
Mark Spencer
committed
p->useragent[0] = '\0';
p->sipoptions = 0;
ast_verbose(VERBOSE_PREFIX_3 "Unregistered SIP '%s'\n", p->name);
manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unregistered\r\n", p->name);
return PARSE_REGISTER_UPDATE;
Kevin P. Fleming
committed
ast_copy_string(p->fullcontact, c, sizeof(p->fullcontact));
/* For the 200 OK, we should use the received contact */
ast_string_field_build(pvt, our_contact, "<%s>", c);
/* Make sure it's a SIP URL */
if (strncasecmp(c, "sip:", 4)) {
ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", c);
} else
c += 4;
/* Grab host */
n = strchr(c, '@');
if (!n) {
n = c;
c = NULL;
} else
*n++ = '\0';
*pt++ = '\0';
port = atoi(pt);
} else
port = DEFAULT_SIP_PORT;
oldsin = p->addr;
if (!ast_test_flag(&p->flags[0], SIP_NAT_ROUTE)) {
/* XXX This could block for a long time XXX */
hp = ast_gethostbyname(n, &ahp);
if (!hp) {
ast_log(LOG_WARNING, "Invalid host '%s'\n", n);
return PARSE_REGISTER_FAILED;
}
p->addr.sin_family = AF_INET;
memcpy(&p->addr.sin_addr, hp->h_addr, sizeof(p->addr.sin_addr));
p->addr.sin_port = htons(port);
} else {
/* Don't trust the contact field. Just use what they came to us
with */
p->addr = pvt->recv;
if (c) /* Overwrite the default username from config at registration */
Kevin P. Fleming
committed
ast_copy_string(p->username, c, sizeof(p->username));
Mark Spencer
committed
p->username[0] = '\0';
if (p->expire > -1)
ast_sched_del(sched, p->expire);
Olle Johansson
committed
if (expiry > max_expiry)
Olle Johansson
committed
if (expiry < min_expiry)
expiry = min_expiry;
p->expire = ast_test_flag(&p->flags[0], SIP_REALTIME) ? -1 :
ast_sched_add(sched, (expiry + 10) * 1000, expire_register, p);
snprintf(data, sizeof(data), "%s:%d:%d:%s:%s", ast_inet_ntoa(iabuf, sizeof(iabuf), p->addr.sin_addr), ntohs(p->addr.sin_port), expiry, p->username, p->fullcontact);
if (!ast_test_flag(&p->flags[1], SIP_PAGE2_RT_FROMCONTACT))
ast_db_put("SIP/Registry", p->name, data);
manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Registered\r\n", p->name);
if (inaddrcmp(&p->addr, &oldsin)) {
Mark Spencer
committed
ast_verbose(VERBOSE_PREFIX_3 "Registered SIP '%s' at %s port %d expires %d\n", p->name, ast_inet_ntoa(iabuf, sizeof(iabuf), p->addr.sin_addr), ntohs(p->addr.sin_port), expiry);
register_peer_exten(p, 1);
/* Save SIP options profile */
p->sipoptions = pvt->sipoptions;
/* Save User agent */
if (useragent && strcasecmp(useragent, p->useragent)) {
Kevin P. Fleming
committed
ast_copy_string(p->useragent, useragent, sizeof(p->useragent));
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_3 "Saved useragent \"%s\" for peer %s\n",p->useragent,p->name);
}
return PARSE_REGISTER_UPDATE;
/*! \brief Remove route from route list */
static void free_old_route(struct sip_route *route)
{
struct sip_route *next;
while (route) {
next = route->next;
free(route);
route = next;
}
}
static void list_route(struct sip_route *route)
{
if (!route)
ast_verbose("list_route: no route\n");
else {
for (;route; route = route->next)
ast_verbose("list_route: hop: <%s>\n", route->hop);
/*! \brief Build route list from Record-Route header */
static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards)
{
struct sip_route *thishop, *head, *tail;
int start = 0;
int len;
const char *rr, *contact, *c;
/* Once a persistant route is set, don't fool with it */
if (p->route && p->route_persistant) {
ast_log(LOG_DEBUG, "build_route: Retaining previous route: <%s>\n", p->route->hop);
return;
}
if (p->route) {
free_old_route(p->route);
p->route = NULL;
}
p->route_persistant = backwards;
/* We build up head, then assign it to p->route when we're done */
head = NULL;
tail = head;
/* 1st we pass through all the hops in any Record-Route headers */
for (;;) {
/* Each Record-Route header */
rr = __get_header(req, "Record-Route", &start);
if (*rr == '\0')
break;
for (;;) {
/* Each route entry */
/* Find < */
rr = strchr(rr, '<');
if (!rr)
break; /* No more hops */
Kevin P. Fleming
committed
len = strcspn(rr, ">") + 1;
if ((thishop = ast_malloc(sizeof(*thishop) + len))) {
/* ast_calloc is not needed because all fields are initialized in this block */
Kevin P. Fleming
committed
ast_copy_string(thishop->hop, rr, len);
ast_log(LOG_DEBUG, "build_route: Record-Route hop: <%s>\n", thishop->hop);
/* Link in */
if (backwards) {
/* Link in at head so they end up in reverse order */
thishop->next = head;
head = thishop;
/* If this was the first then it'll be the tail */
if (!tail) tail = thishop;
} else {
/* Link in at the end */
if (tail)
tail->next = thishop;
else
head = thishop;
tail = thishop;
}
}
Kevin P. Fleming
committed
rr += len;
/* Only append the contact if we are dealing with a strict router */
if (!head || (!ast_strlen_zero(head->hop) && strstr(head->hop,";lr") == NULL) ) {
/* 2nd append the Contact: if there is one */
/* Can be multiple Contact headers, comma separated values - we just take the first */
contact = get_header(req, "Contact");
if (!ast_strlen_zero(contact)) {
ast_log(LOG_DEBUG, "build_route: Contact hop: %s\n", contact);
/* Look for <: delimited address */
c = strchr(contact, '<');
if (c) {
/* Take to > */
++c;
len = strcspn(c, ">") + 1;
} else {
/* No <> - just take the lot */
c = contact;
len = strlen(contact) + 1;
}
if ((thishop = ast_malloc(sizeof(*thishop) + len))) {
/* ast_calloc is not needed because all fields are initialized in this block */
ast_copy_string(thishop->hop, c, len);
thishop->next = NULL;
/* Goes at the end */
if (tail)
tail->next = thishop;
else
head = thishop;
}
/* Store as new route */
p->route = head;
/* For debugging dump what we ended up with */
if (sip_debug_test_pvt(p))
list_route(p->route);
}
/*! \brief Check user authorization from peer definition
Some actions, like REGISTER and INVITEs from peers require
authentication (if peer have secret set)
\return -1 on Error, 0 on success, 1 on challenge sent
*/
static int check_auth(struct sip_pvt *p, struct sip_request *req, const char *username,
const char *secret, const char *md5secret, int sipmethod,
char *uri, enum xmittype reliable, int ignore)
const char *response = "407 Proxy Authentication Required";
const char *reqheader = "Proxy-Authorization";
const char *respheader = "Proxy-Authenticate";
const char *authtoken;
if (ast_strlen_zero(secret) && ast_strlen_zero(md5secret))
Kevin P. Fleming
committed
if (sipmethod == SIP_REGISTER || sipmethod == SIP_SUBSCRIBE) {
Mark Spencer
committed
/* On a REGISTER, we have to use 401 and its family of headers instead of 407 and its family
of headers -- GO SIP! Whoo hoo! Two things that do the same thing but are used in
different circumstances! What a surprise. */
response = "401 Unauthorized";
reqheader = "Authorization";
respheader = "WWW-Authenticate";
}
authtoken = get_header(req, reqheader);
if (ignore && !ast_strlen_zero(p->randdata) && ast_strlen_zero(authtoken)) {
/* This is a retransmitted invite/register/etc, don't reconstruct authentication
information */
if (!reliable) {
/* Resend message if this was NOT a reliable delivery. Otherwise the
retransmission should get it */
transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
/* Schedule auto destroy in 32 seconds (according to RFC 3261) */
sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
} else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) {
/* We have no auth, so issue challenge and request authentication */
Tilghman Lesher
committed
ast_string_field_build(p, randdata, "%08lx", ast_random()); /* Create nonce for challenge */
transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
return 1; /* Auth sent */
} else { /* We have auth, so check it */
/* Whoever came up with the authentication section of SIP can suck my %&#$&* for not putting
an example in the spec of just what it is you're doing a hash on. */
Mark Spencer
committed
char resp_hash[256]="";
int good_response;
const char *usednonce = p->randdata; /* XXX check */
/* table of recognised keywords, and their value in the digest */
enum keys { K_RESP, K_URI, K_USER, K_NONCE, K_LAST };
struct x {
const char *key;
const char *s;
} *i, keys[] = {
[K_RESP] = { "response=", "" },
[K_URI] = { "uri=", "" },
[K_USER] = { "username=", "" },
[K_NONCE] = { "nonce=", "" },
[K_LAST] = { NULL, NULL}
};
/* Make a copy of the response and parse it */
Kevin P. Fleming
committed
ast_copy_string(tmp, authtoken, sizeof(tmp));
while(c && *(c = ast_skip_blanks(c)) ) { /* lookup for keys */
for (i = keys; i->key != NULL; i++) {
const char *separator = ","; /* default */
if (strncasecmp(c, i->key, strlen(i->key)) != 0)
continue;
/* Found. Skip keyword, take text in quotes or up to the separator. */
c += strlen(i->key);
if (*c == '"') { /* in quotes. Skip first and look for last */
c++;
separator = "\"";
Kevin P. Fleming
committed
}
i->s = c;
strsep(&c, separator);
break;
}
if (i->key == NULL) /* not found, jump after space or comma */
strsep(&c, " ,");
Kevin P. Fleming
committed
/* Verify that digest username matches the username we auth as */
if (strcmp(username, keys[K_USER].s)) {
ast_log(LOG_WARNING, "username mismatch, have <%s>, digest has <%s>\n",
username, keys[K_USER].s);
Kevin P. Fleming
committed
/* Oops, we're trying something here */
return -2;
}
/* Verify nonce from request matches our nonce. If not, send 401 with new nonce */
if (strcasecmp(p->randdata, keys[K_NONCE].s)) { /* XXX it was 'n'casecmp ? */
Kevin P. Fleming
committed
}
if (!ast_strlen_zero(md5secret))
ast_copy_string(a1_hash, md5secret, sizeof(a1_hash));
else {
char a1[256];
snprintf(a1, sizeof(a1), "%s:%s:%s", username, global_realm, secret);
Kevin P. Fleming
committed
ast_md5_hash(a1_hash, a1);
Kevin P. Fleming
committed
/* compute the expected response to compare with what we received */
{
char a2[256];
char a2_hash[256];
char resp[256];
snprintf(a2, sizeof(a2), "%s:%s", sip_methods[sipmethod].text,
ast_md5_hash(a2_hash, a2);
snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, usednonce, a2_hash);
ast_md5_hash(resp_hash, resp);
}
good_response = keys[K_RESP].s &&
!strncasecmp(keys[K_RESP].s, resp_hash, strlen(resp_hash));
Kevin P. Fleming
committed
if (wrongnonce) {
Tilghman Lesher
committed
ast_string_field_build(p, randdata, "%08lx", ast_random());
Russell Bryant
committed
if (sipdebug)
ast_log(LOG_NOTICE, "stale nonce received from '%s'\n", get_header(req, "To"));
Kevin P. Fleming
committed
/* We got working auth token, based on stale nonce . */
transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 1);
Kevin P. Fleming
committed
} else {
/* Everything was wrong, so give the device one more try with a new challenge */
Russell Bryant
committed
if (sipdebug)
ast_log(LOG_NOTICE, "Bad authentication received from '%s'\n", get_header(req, "To"));
transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
Kevin P. Fleming
committed
}
sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
return 1; /* XXX should it be -1 ? */
Kevin P. Fleming
committed
}
Olle Johansson
committed
/* XXX is this needed ? */
Olle Johansson
committed
/* Ok, we have a bad username/secret pair */
/* Challenge again, and again, and again */
transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
Olle Johansson
committed
return 1; /* Challenge sent */
/*! \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 */
Kevin P. Fleming
committed
static int cb_extensionstate(char *context, char* exten, int state, void *data)
Kevin P. Fleming
committed
Kevin P. Fleming
committed
switch(state) {
case AST_EXTENSION_DEACTIVATED: /* Retry after a while */
case AST_EXTENSION_REMOVED: /* Extension is gone */
Kevin P. Fleming
committed
if (p->autokillid > -1)
sip_cancel_destroy(p); /* Remove subscription expiry for renewals */
sip_scheddestroy(p, SIP_TRANS_TIMEOUT); /* Delete subscription in 32 secs */
Kevin P. Fleming
committed
ast_verbose(VERBOSE_PREFIX_2 "Extension state: Watcher for hint %s %s. Notify User %s\n", exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username);
Kevin P. Fleming
committed
p->subscribed = NONE;
Olle Johansson
committed
append_history(p, "Subscribestatus", "%s", state == AST_EXTENSION_REMOVED ? "HintRemoved" : "Deactivated");
Kevin P. Fleming
committed
break;
default: /* Tell user */
p->laststate = state;
break;
transmit_state_notify(p, state, 1);
Kevin P. Fleming
committed
if (option_debug > 1)
ast_verbose(VERBOSE_PREFIX_1 "Extension Changed %s new state %s for Notify User %s\n", exten, ast_extension_state2str(state), p->username);
return 0;
static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct sip_request *req, char *uri)
char iabuf[INET_ADDRSTRLEN];
char *domain;
Kevin P. Fleming
committed
/* Terminate URI */
t = uri;
while(*t && (*t > 32) && (*t != ';'))
t++;
*t = '\0';
Kevin P. Fleming
committed
ast_copy_string(tmp, get_header(req, "To"), sizeof(tmp));
if (pedanticsipchecking)
ast_uri_decode(tmp);
Kevin P. Fleming
committed
c = get_in_brackets(tmp);
/* Ditch ;user=phone */
name = strchr(c, ';');
if (name)
*name = '\0';
if (!strncmp(c, "sip:", 4)) {
name = c + 4;
} else {
name = c;
Mark Spencer
committed
ast_log(LOG_NOTICE, "Invalid to address: '%s' from %s (missing sip:) trying to use anyway...\n", c, ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr));
/* Strip off the domain name */
if ((c = strchr(name, '@'))) {
*c++ = '\0';
domain = c;
if ((c = strchr(domain, ':'))) /* Remove :port */
*c = '\0';
if (!AST_LIST_EMPTY(&domain_list)) {
if (!check_sip_domain(domain, NULL, 0)) {
transmit_response(p, "404 Not found (unknown domain)", &p->initreq);
return -3;
}
}
}
ast_string_field_set(p, exten, name);
peer = find_peer(name, NULL, 1);
if (!(peer && ast_apply_ha(peer->ha, sin))) {
ASTOBJ_UNREF(peer, sip_destroy_peer);
if (!ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC)) {
Kevin P. Fleming
committed
ast_log(LOG_ERROR, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name);
ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_NAT);
transmit_response(p, "100 Trying", req);
if (!(res = check_auth(p, req, peer->name, peer->secret, peer->md5secret, SIP_REGISTER, uri, XMIT_UNRELIABLE, ast_test_flag(req, SIP_PKT_IGNORE)))) {
switch (parse_register_contact(p, peer, req)) {
case PARSE_REGISTER_FAILED:
ast_log(LOG_WARNING, "Failed to parse contact info\n");
transmit_response_with_date(p, "400 Bad Request", req);
peer->lastmsgssent = -1;
res = 0;
break;
case PARSE_REGISTER_QUERY:
transmit_response_with_date(p, "200 OK", req);
peer->lastmsgssent = -1;
res = 0;
break;
case PARSE_REGISTER_UPDATE:
update_peer(peer, p->expiry);
/* Say OK and ask subsystem to retransmit msg counter */
transmit_response_with_date(p, "200 OK", req);
Kevin P. Fleming
committed
if (!ast_test_flag((&peer->flags[1]), SIP_PAGE2_SUBSCRIBEMWIONLY))
peer->lastmsgssent = -1;
if (!peer && autocreatepeer) {
/* Create peer if we have autocreate mode enabled */
peer = temp_peer(name);
if (peer) {
Mark Spencer
committed
ASTOBJ_CONTAINER_LINK(&peerl, peer);
switch (parse_register_contact(p, peer, req)) {
case PARSE_REGISTER_FAILED:
ast_log(LOG_WARNING, "Failed to parse contact info\n");
transmit_response_with_date(p, "400 Bad Request", req);
peer->lastmsgssent = -1;
res = 0;
break;
case PARSE_REGISTER_QUERY:
transmit_response_with_date(p, "200 OK", req);
peer->lastmsgssent = -1;
res = 0;
break;
case PARSE_REGISTER_UPDATE:
/* Say OK and ask subsystem to retransmit msg counter */
transmit_response_with_date(p, "200 OK", req);
manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Registered\r\n", peer->name);
peer->lastmsgssent = -1;
res = 0;
ast_device_state_changed("SIP/%s", peer->name);
Kevin P. Fleming
committed
if (res < 0) {
switch (res) {
case -1:
/* Wrong password in authentication. Go away, don't try again until you fixed it */
transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
Kevin P. Fleming
committed
break;
case -2:
/* Username and digest username does not match.
Asterisk uses the From: username for authentication. We need the
users to use the same authentication user name until we support
proper authentication by digest auth name */
transmit_response(p, "403 Authentication user name does not match account name", &p->initreq);
break;
case -3:
/* URI not found */
transmit_response(p, "404 Not found", &p->initreq);
/* Set res back to -2 because we don't want to return an invalid domain message. That check already happened up above. */
res = -2;
break;
}
if (option_debug > 1) {
ast_log(LOG_DEBUG, "SIP REGISTER attempt failed for %s : %s\n",
peer->name,
(res == -1) ? "Bad password" : ((res == -2 ) ? "Bad digest user" : "Peer not found"));
Kevin P. Fleming
committed
}
}
Mark Spencer
committed
if (peer)
ASTOBJ_UNREF(peer, sip_destroy_peer);
static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq)
{
char tmp[256], *c, *a;
struct sip_request *req;
req = oreq;
if (!req)
req = &p->initreq;
Kevin P. Fleming
committed
ast_copy_string(tmp, get_header(req, "Diversion"), sizeof(tmp));
if (ast_strlen_zero(tmp))
Kevin P. Fleming
committed
c = get_in_brackets(tmp);
if (strncmp(c, "sip:", 4)) {
ast_log(LOG_WARNING, "Huh? Not an RDNIS SIP header (%s)?\n", c);
return -1;
}
c += 4;
a = c;
strsep(&a, "@;"); /* trim anything after @ or ; */
if (sip_debug_test_pvt(p))
ast_string_field_set(p, rdnis, c);
/*! \brief Find out who the call is for
We use the INVITE uri to find out
*/
static int get_destination(struct sip_pvt *p, struct sip_request *oreq)
char tmp[256] = "", *uri, *a;
char tmpf[256] = "", *from;
Olle Johansson
committed
char *colon;
req = oreq;
if (!req)
req = &p->initreq;
/* Find the request URI */
Kevin P. Fleming
committed
ast_copy_string(tmp, req->rlPart2, sizeof(tmp));
uri = get_in_brackets(tmp);
if (pedanticsipchecking)
ast_uri_decode(tmp);
if (strncmp(uri, "sip:", 4)) {
ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", uri);
uri += 4;
/* Now find the From: caller ID and name */
ast_copy_string(tmpf, get_header(req, "From"), sizeof(tmpf));
if (!ast_strlen_zero(tmpf)) {
if (pedanticsipchecking)
ast_uri_decode(tmpf);
from = get_in_brackets(tmpf);
} else {
from = NULL;
}
if (!ast_strlen_zero(from)) {
if (strncmp(from, "sip:", 4)) {
ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", from);
from += 4;
if ((a = strchr(from, ';')))
*a = '\0';
if ((a = strchr(from, '@'))) {
*a = '\0';
ast_string_field_set(p, fromdomain, a + 1);
} else
ast_string_field_set(p, fromdomain, from);
/* Skip any options and find the domain */
if ((a = strchr(uri, ';')))
Olle Johansson
committed
*a = '\0';
/* Get the target domain */
if ((a = strchr(uri, '@'))) {
*a++ = '\0';
colon = strchr(a, ':'); /* Remove :port */
if (colon)
*colon = '\0';
Olle Johansson
committed
} else { /* No username part */
a = uri;
uri = "s"; /* Set extension to "s" */
Olle Johansson
committed
colon = strchr(a, ':'); /* Remove :port */
if (colon)
*colon = '\0';
ast_string_field_set(p, domain, a);
if (!AST_LIST_EMPTY(&domain_list)) {
char domain_context[AST_MAX_EXTENSION];
domain_context[0] = '\0';
if (!check_sip_domain(p->domain, domain_context, sizeof(domain_context))) {
if (!allow_external_domains && (req->method == SIP_INVITE || req->method == SIP_REFER)) {
ast_log(LOG_DEBUG, "Got SIP %s to non-local domain '%s'; refusing request.\n", sip_methods[req->method].text, p->domain);
return -2;
}
}
/* If we have a context defined, overwrite the original context */
if (!ast_strlen_zero(domain_context))
ast_string_field_set(p, context, domain_context);
if (sip_debug_test_pvt(p))
ast_verbose("Looking for %s in %s (domain %s)\n", uri, p->context, p->domain);
/* Check the dialplan for the username part of the request URI,
the domain will be stored in the SIPDOMAIN variable
Return 0 if we have a matching extension */
if (ast_exists_extension(NULL, p->context, uri, 1, from) ||
!strcmp(uri, ast_pickup_ext())) {
if (!oreq)
ast_string_field_set(p, exten, uri);