summaryrefslogtreecommitdiff
path: root/channels
diff options
context:
space:
mode:
authorMark Michelson <mmichelson@digium.com>2009-07-31 17:55:44 +0000
committerMark Michelson <mmichelson@digium.com>2009-07-31 17:55:44 +0000
commit2df5b70b168f8ec09b514ea692b5c57f3d68e629 (patch)
tree9646cc8362b79bff82cc38ea5eacd59e848dfce3 /channels
parentc3ab548cd862597e6241a5309b81d25a5efd08b8 (diff)
Improve chan_sip's ability to determine what methods should and should not be used in a dialog.
The previous effort here was to store what a peer is capable of receiving by parsing REGISTER requests from the peer and keeping that information for as long as the registration was active. The problem with this is that there are a great number of SIP devices which give no indication of the methods allowed in their REGISTER requests, and it is unreasonable to try to guess what the device may or may not support. In addition, some SIP devices have been found to claim support for a specific method, but their handling the method is less than ideal, or they are actually lying. With this patch, we now determine what methods a device supports by parsing the Allow header we receive from them, and we do this with each new dialog. In addition, a configuration option has been added so that an administrator can essentially blacklist certain methods from being used with certain peers if the admin knows that support for a specific method is dodgy or nonexistent. ABE-1822 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@209673 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'channels')
-rw-r--r--channels/chan_sip.c113
1 files changed, 46 insertions, 67 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 372d8d942..5e777b4c3 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1187,6 +1187,7 @@ struct sip_settings {
int callevents; /*!< Whether we send manager events or not */
int regextenonqualify; /*!< Whether to add/remove regexten when qualifying peers */
int matchexterniplocally; /*!< Match externip/externhost setting against localnet setting */
+ unsigned int disallowed_methods; /*!< methods that we should never try to use */
int notifyringing; /*!< Send notifications on ringing */
int notifyhold; /*!< Send notifications on hold */
enum notifycid_setting notifycid; /*!< Send CID with ringing notifications */
@@ -1817,11 +1818,15 @@ struct sip_pvt {
int hangupcause; /*!< Storage of hangupcause copied from our owner before we disconnect from the AST channel (only used at hangup) */
struct sip_subscription_mwi *mwi; /*!< If this is a subscription MWI dialog, to which subscription */
- /*! The SIP methods allowed on this dialog. We get this information from the Allow header present in
- * the peer's REGISTER. If peer does not register with us, then we will use the first transaction we
- * have with this peer to determine its allowed methods.
+ /*! The SIP methods supported by this peer. We get this information from the Allow header of the first
+ * message we receive from an endpoint during a dialog.
*/
unsigned int allowed_methods;
+ /*! Some peers are not trustworthy with their Allow headers, and so we need to override their wicked
+ * ways through configuration. This is a copy of the peer's disallowed_methods, so that we can apply them
+ * to the sip_pvt at various stages of dialog establishment
+ */
+ unsigned int disallowed_methods;
/*! When receiving an SDP offer, it is important to take note of what media types were offered.
* By doing this, even if we don't want to answer a particular media stream with something meaningful, we can
* still put an m= line in our answer with the port set to 0.
@@ -2027,7 +2032,7 @@ struct sip_peer {
/*XXX Seems like we suddenly have two flags with the same content. Why? To be continued... */
enum sip_peer_type type; /*!< Distinguish between "user" and "peer" types. This is used solely for CLI and manager commands */
- unsigned int allowed_methods;
+ unsigned int disallowed_methods;
};
@@ -5107,7 +5112,7 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
dialog->rtptimeout = peer->rtptimeout;
dialog->peerauth = peer->auth;
dialog->maxcallbitrate = peer->maxcallbitrate;
- dialog->allowed_methods = peer->allowed_methods;
+ dialog->disallowed_methods = peer->disallowed_methods;
if (ast_strlen_zero(dialog->tohost))
ast_string_field_set(dialog, tohost, ast_inet_ntoa(dialog->sa.sin_addr));
if (!ast_strlen_zero(peer->fromdomain)) {
@@ -5201,6 +5206,7 @@ static int create_addr(struct sip_pvt *dialog, const char *opeer, struct sockadd
}
ast_string_field_set(dialog, tohost, peername);
+ dialog->allowed_methods &= ~sip_cfg.disallowed_methods;
/* Get the outbound proxy information */
ref_proxy(dialog, obproxy_get(dialog, NULL));
@@ -7096,6 +7102,7 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si
p->branch = ast_random();
make_our_tag(p->tag, sizeof(p->tag));
p->ocseq = INITIAL_CSEQ;
+ p->allowed_methods = UINT_MAX;
if (sip_methods[intended_method].need_rtp) {
if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && (p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr))) {
@@ -7521,6 +7528,17 @@ static int is_method_allowed(unsigned int *allowed_methods, enum sipmethod metho
return ((*allowed_methods) >> method) & 1;
}
+static void mark_parsed_methods(unsigned int *methods, char *methods_str)
+{
+ char *method;
+ for (method = strsep(&methods_str, ","); !ast_strlen_zero(method); method = strsep(&methods_str, ",")) {
+ int id = find_sip_method(ast_skip_blanks(method));
+ if (id == SIP_UNKNOWN) {
+ continue;
+ }
+ mark_method_allowed(methods, id);
+ }
+}
/*!
* \brief parse the Allow header to see what methods the endpoint we
* are communicating with allows.
@@ -7540,7 +7558,6 @@ static int is_method_allowed(unsigned int *allowed_methods, enum sipmethod metho
static unsigned int parse_allowed_methods(struct sip_request *req)
{
char *allow = ast_strdupa(get_header(req, "Allow"));
- char *method;
unsigned int allowed_methods = SIP_UNKNOWN;
if (ast_strlen_zero(allow)) {
@@ -7567,13 +7584,7 @@ static unsigned int parse_allowed_methods(struct sip_request *req)
}
allow = ast_strip_quoted(methods + 9, "\"", "\"");
}
- for (method = strsep(&allow, ","); !ast_strlen_zero(method); method = strsep(&allow, ",")) {
- int id = find_sip_method(ast_skip_blanks(method));
- if (id == SIP_UNKNOWN) {
- continue;
- }
- mark_method_allowed(&allowed_methods, id);
- }
+ mark_parsed_methods(&allowed_methods, allow);
return allowed_methods;
}
@@ -7593,6 +7604,7 @@ static unsigned int set_pvt_allowed_methods(struct sip_pvt *pvt, struct sip_requ
if (ast_test_flag(&pvt->flags[1], SIP_PAGE2_RPID_UPDATE)) {
mark_method_allowed(&pvt->allowed_methods, SIP_UPDATE);
}
+ pvt->allowed_methods &= ~(pvt->disallowed_methods);
return pvt->allowed_methods;
}
@@ -11876,7 +11888,6 @@ static int expire_register(const void *data)
manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
register_peer_exten(peer, FALSE); /* Remove regexten */
ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name);
- peer->allowed_methods = SIP_UNKNOWN;
/* Do we need to release this peer from memory?
Only for realtime peers and autocreated peers
@@ -11919,13 +11930,11 @@ static void reg_source_db(struct sip_peer *peer)
int expire;
int port;
char *scan, *addr, *port_str, *expiry_str, *username, *contact;
- char allowed_methods_str[256] = "";
if (peer->rt_fromcontact)
return;
if (ast_db_get("SIP/Registry", peer->name, data, sizeof(data)))
return;
- ast_db_get("SIP/PeerMethods", peer->name, allowed_methods_str, sizeof(allowed_methods_str));
scan = data;
addr = strsep(&scan, ":");
@@ -11952,10 +11961,6 @@ static void reg_source_db(struct sip_peer *peer)
if (contact)
ast_string_field_set(peer, fullcontact, contact);
- if (!ast_strlen_zero(allowed_methods_str)) {
- peer->allowed_methods = atoi(allowed_methods_str);
- }
-
ast_debug(2, "SIP Seeding peer from astdb: '%s' at %s@%s:%d for %d\n",
peer->name, peer->username, ast_inet_ntoa(in), port, expire);
@@ -13003,16 +13008,6 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr
}
}
if (peer) {
- ao2_lock(peer);
- if (peer->allowed_methods == SIP_UNKNOWN) {
- peer->allowed_methods = set_pvt_allowed_methods(p, req);
- }
- if (!peer->rt_fromcontact) {
- char allowed_methods_str[256];
- snprintf(allowed_methods_str, sizeof(allowed_methods_str), "%u", peer->allowed_methods);
- ast_db_put("SIP/PeerMethods", peer->name, allowed_methods_str);
- }
- ao2_unlock(peer);
unref_peer(peer, "register_verify: unref_peer: tossing stack peer pointer at end of func");
}
@@ -13988,11 +13983,8 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
ast_string_field_set(p, mohsuggest, peer->mohsuggest);
ast_string_field_set(p, parkinglot, peer->parkinglot);
ast_string_field_set(p, engine, peer->engine);
- if (peer->allowed_methods == SIP_UNKNOWN) {
- set_pvt_allowed_methods(p, req);
- } else {
- p->allowed_methods = peer->allowed_methods;
- }
+ p->disallowed_methods = peer->disallowed_methods;
+ set_pvt_allowed_methods(p, req);
if (peer->callingpres) /* Peer calling pres setting will override RPID */
p->callingpres = peer->callingpres;
if (peer->maxms && peer->lastms)
@@ -17677,21 +17669,6 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
else
ast_debug(4, "SIP response %d to standard invite\n", resp);
- /* If this is a response to our initial INVITE, we need to set what we can use
- * for this peer.
- */
- if (!reinvite && p->allowed_methods == SIP_UNKNOWN) {
- struct sip_peer *peer = find_peer(p->peername, NULL, 1, FINDPEERS, FALSE);
- if (!peer || peer->allowed_methods == SIP_UNKNOWN) {
- set_pvt_allowed_methods(p, req);
- } else {
- p->allowed_methods = peer->allowed_methods;
- }
- if (peer) {
- unref_peer(peer, "handle_response_invite: Getting supported methods from peer");
- }
- }
-
if (p->alreadygone) { /* This call is already gone */
ast_debug(1, "Got response on call that is already terminated: %s (ignoring)\n", p->callid);
return;
@@ -17719,6 +17696,13 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
if ((resp == 200 || resp >= 300) && p->pendinginvite && seqno == p->pendinginvite)
p->pendinginvite = 0;
+ /* If this is a response to our initial INVITE, we need to set what we can use
+ * for this peer.
+ */
+ if (!reinvite) {
+ set_pvt_allowed_methods(p, req);
+ }
+
switch (resp) {
case 100: /* Trying */
case 101: /* Dialog establishment */
@@ -18087,7 +18071,6 @@ static void handle_response_notify(struct sip_pvt *p, int resp, const char *rest
/* \brief Handle SIP response in SUBSCRIBE transaction */
static void handle_response_subscribe(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno)
{
- struct sip_peer *peer;
if (!p->mwi) {
return;
}
@@ -18095,15 +18078,7 @@ static void handle_response_subscribe(struct sip_pvt *p, int resp, const char *r
switch (resp) {
case 200: /* Subscription accepted */
ast_debug(3, "Got 200 OK on subscription for MWI\n");
- peer = find_peer(p->peername, NULL, 1, FINDPEERS, FALSE);
- if (!peer || peer->allowed_methods == SIP_UNKNOWN) {
- set_pvt_allowed_methods(p, req);
- } else {
- p->allowed_methods = peer->allowed_methods;
- }
- if (peer) {
- unref_peer(peer, "handle_response_subscribe: Getting supported methods");
- }
+ set_pvt_allowed_methods(p, req);
if (p->options) {
ast_free(p->options);
p->options = NULL;
@@ -18422,12 +18397,6 @@ static void handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_req
manager_event(EVENT_FLAG_SYSTEM, "PeerStatus",
"ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: %s\r\nTime: %d\r\n",
peer->name, s, pingtime);
- if (!is_reachable) {
- peer->allowed_methods = SIP_UNKNOWN;
- } else {
- set_pvt_allowed_methods(p, req);
- peer->allowed_methods = p->allowed_methods;
- }
if (is_reachable && sip_cfg.regextenonqualify)
register_peer_exten(peer, TRUE);
}
@@ -18682,7 +18651,7 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
case 501: /* Not Implemented */
mark_method_unallowed(&p->allowed_methods, sipmethod);
if ((peer = find_peer(p->peername, 0, 1, FINDPEERS, FALSE))) {
- peer->allowed_methods = p->allowed_methods;
+ mark_method_allowed(&peer->disallowed_methods, sipmethod);
unref_peer(peer, "handle_response: marking a specific method as unallowed");
}
if (sipmethod == SIP_INVITE)
@@ -20209,6 +20178,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
/* This is a new invite */
/* Handle authentication if this is our first invite */
struct ast_party_redirecting redirecting = {{0,},};
+ set_pvt_allowed_methods(p, req);
res = check_user(p, req, SIP_INVITE, e, XMIT_RELIABLE, sin);
if (res == AUTH_CHALLENGE_SENT) {
p->invitestate = INV_COMPLETED; /* Needs to restart in another INVITE transaction */
@@ -21494,6 +21464,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
if (!req->ignore && !resubscribe) { /* Set up dialog, new subscription */
const char *to = get_header(req, "To");
char totag[128];
+ set_pvt_allowed_methods(p, req);
/* Check to see if a tag was provided, if so this is actually a resubscription of a dialog we no longer know about */
if (!ast_strlen_zero(to) && gettag(req, "To", totag, sizeof(totag))) {
@@ -23830,6 +23801,7 @@ static void set_peer_defaults(struct sip_peer *peer)
peer->timer_t1 = global_t1;
peer->timer_b = global_timer_b;
clear_peer_mailboxes(peer);
+ peer->disallowed_methods = sip_cfg.disallowed_methods;
}
/*! \brief Create temporary peer (used in autocreatepeer mode) */
@@ -24285,6 +24257,9 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
} else {
peer->stimer.st_ref = i;
}
+ } else if (!strcasecmp(v->name, "disallowed_methods")) {
+ char *disallow = ast_strdupa(v->value);
+ mark_parsed_methods(&peer->disallowed_methods, disallow);
}
}
@@ -24572,6 +24547,7 @@ static int reload_config(enum channelreloadreason reason)
sip_cfg.directrtpsetup = FALSE; /* Experimental feature, disabled by default */
sip_cfg.alwaysauthreject = DEFAULT_ALWAYSAUTHREJECT;
sip_cfg.allowsubscribe = FALSE;
+ sip_cfg.disallowed_methods = SIP_UNKNOWN;
snprintf(global_useragent, sizeof(global_useragent), "%s %s", DEFAULT_USERAGENT, ast_get_version());
snprintf(global_sdpsession, sizeof(global_sdpsession), "%s %s", DEFAULT_SDPSESSION, ast_get_version());
snprintf(global_sdpowner, sizeof(global_sdpowner), "%s", DEFAULT_SDPOWNER);
@@ -25064,6 +25040,9 @@ static int reload_config(enum channelreloadreason reason)
ast_log(LOG_WARNING, "Invalid pokepeers '%s' at line %d of %s\n", v->value, v->lineno, config);
global_qualify_peers = DEFAULT_QUALIFY_PEERS;
}
+ } else if (!strcasecmp(v->name, "disallowed_methods")) {
+ char *disallow = ast_strdupa(v->value);
+ mark_parsed_methods(&sip_cfg.disallowed_methods, disallow);
}
}