[svn-commits] russell: branch group/security_events r195630 - in /team/group/security_event...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Tue May 19 17:12:12 CDT 2009


Author: russell
Date: Tue May 19 17:12:09 2009
New Revision: 195630

URL: http://svn.asterisk.org/svn-view/asterisk?view=rev&rev=195630
Log:
resolve, reset

Modified:
    team/group/security_events/   (props changed)
    team/group/security_events/channels/chan_sip.c
    team/group/security_events/configs/sip.conf.sample

Propchange: team/group/security_events/
------------------------------------------------------------------------------
    automerge = *

Propchange: team/group/security_events/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Tue May 19 17:12:09 2009
@@ -1,1 +1,1 @@
-/trunk:1-195542
+/trunk:1-195629

Modified: team/group/security_events/channels/chan_sip.c
URL: http://svn.asterisk.org/svn-view/asterisk/team/group/security_events/channels/chan_sip.c?view=diff&rev=195630&r1=195629&r2=195630
==============================================================================
--- team/group/security_events/channels/chan_sip.c (original)
+++ team/group/security_events/channels/chan_sip.c Tue May 19 17:12:09 2009
@@ -1424,6 +1424,7 @@
 /* realtime flags */
 #define SIP_PAGE2_RTCACHEFRIENDS	(1 << 0)	/*!< GP: Should we keep RT objects in memory for extended time? */
 #define SIP_PAGE2_RTAUTOCLEAR		(1 << 2)	/*!< GP: Should we clean memory from peers after expiry? */
+#define SIP_PAGE2_RPID_UPDATE		(1 << 3)
 /* Space for addition of other realtime flags in the future */
 #define SIP_PAGE2_STATECHANGEQUEUE	(1 << 9)	/*!< D: Unsent state pending change exists */
 
@@ -1461,7 +1462,7 @@
 	SIP_PAGE2_VIDEOSUPPORT | SIP_PAGE2_T38SUPPORT | SIP_PAGE2_RFC2833_COMPENSATE | \
 	SIP_PAGE2_BUGGY_MWI | SIP_PAGE2_TEXTSUPPORT | SIP_PAGE2_FAX_DETECT | \
 	SIP_PAGE2_UDPTL_DESTINATION | SIP_PAGE2_VIDEOSUPPORT_ALWAYS | SIP_PAGE2_PREFERRED_CODEC | \
-	SIP_PAGE2_RPID_IMMEDIATE)
+	SIP_PAGE2_RPID_IMMEDIATE | SIP_PAGE2_RPID_UPDATE)
 
 /*@}*/ 
 
@@ -1782,7 +1783,12 @@
 
 	struct sip_subscription_mwi *mwi;       /*!< If this is a subscription MWI dialog, to which subscription */
 	struct timeval session_tv;		/*!< The time this dialog started */
-};
+	/*! 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.
+	 */
+	unsigned int allowed_methods;
+}; 
 
 
 /*! \brief
@@ -1968,6 +1974,7 @@
 	
 	/*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;
 };
 
 
@@ -2548,6 +2555,8 @@
 static const char *gettag(const struct sip_request *req, const char *header, char *tagbuf, int tagbufsize);
 static int find_sip_method(const char *msg);
 static unsigned int parse_sip_options(struct sip_pvt *pvt, const char *supported);
+static unsigned int parse_allowed_methods(struct sip_request *req);
+static unsigned int set_pvt_allowed_methods(struct sip_pvt *pvt, struct sip_request *req);
 static int parse_request(struct sip_request *req);
 static const char *get_header(const struct sip_request *req, const char *name);
 static const char *referstatus2str(enum referstatus rstatus) attribute_pure;
@@ -2611,6 +2620,7 @@
 
 /*------Request handling functions */
 static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int *recount, int *nounlock);
+static int handle_request_update(struct sip_pvt *p, struct sip_request *req);
 static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *recount, char *e, int *nounlock);
 static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, int *nounlock);
 static int handle_request_bye(struct sip_pvt *p, struct sip_request *req);
@@ -4917,6 +4927,7 @@
 	dialog->rtptimeout = peer->rtptimeout;
 	dialog->peerauth = peer->auth;
 	dialog->maxcallbitrate = peer->maxcallbitrate;
+	dialog->allowed_methods = peer->allowed_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)) {
@@ -7240,6 +7251,87 @@
 	ASTOBJ_UNREF(mwi, sip_subscribe_mwi_destroy);
 	
 	return 0;
+}
+
+static void mark_method_allowed(unsigned int *allowed_methods, enum sipmethod method)
+{
+	(*allowed_methods) |= (1 << method);
+}
+
+static void mark_method_unallowed(unsigned int *allowed_methods, enum sipmethod method)
+{
+	(*allowed_methods) &= ~(1 << method);
+}
+
+static int is_method_allowed(unsigned int *allowed_methods, enum sipmethod method)
+{
+	return ((*allowed_methods) >> method) & 1;
+}
+
+/*!
+ * \brief parse the Allow header to see what methods the endpoint we
+ * are communicating with allows.
+ *
+ * We parse the allow header on incoming Registrations and save the
+ * result to the SIP peer that is registering. When the registration
+ * expires, we clear what we know about the peer's allowed methods.
+ * When the peer re-registers, we once again parse to see if the 
+ * list of allowed methods has changed.
+ *
+ * For peers that do not register, we parse the first message we receive
+ * during a call to see what is allowed, and save the information
+ * for the duration of the call.
+ * \param req The SIP request we are parsing
+ * \retval The methods allowed
+ */
+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)) {
+		/* RFC 3261 states:
+		 *
+		 * "The absence of an Allow header field MUST NOT be
+		 * interpreted to mean that the UA sending the message supports no
+		 * methods.   Rather, it implies that the UA is not providing any
+		 * information on what methods it supports."
+		 *
+		 * For simplicity, we'll assume that the peer allows all known
+		 * SIP methods if they have no Allow header. We can then clear out the necessary
+		 * bits if the peer lets us know that we have sent an unsupported method.
+		 */
+		return UINT_MAX;
+	}
+	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);
+	}
+	return allowed_methods;
+}
+
+/*! A wrapper for parse_allowed_methods geared toward sip_pvts
+ *
+ * This function, in addition to setting the allowed methods for a sip_pvt
+ * also will take into account the setting of the SIP_PAGE2_RPID_UPDATE flag.
+ *
+ * \param pvt The sip_pvt we are setting the allowed_methods for
+ * \param req The request which we are parsing
+ * \retval The methods alloweded by the sip_pvt
+ */
+static unsigned int set_pvt_allowed_methods(struct sip_pvt *pvt, struct sip_request *req)
+{
+	pvt->allowed_methods = parse_allowed_methods(req);
+	
+	if (ast_test_flag(&pvt->flags[1], SIP_PAGE2_RPID_UPDATE)) {
+		mark_method_allowed(&pvt->allowed_methods, SIP_UPDATE);
+	}
+
+	return pvt->allowed_methods;
 }
 
 /*! \brief  Parse multiline SIP headers into one header
@@ -9879,8 +9971,12 @@
 			add_header(&req, "X-asterisk-Info", "SIP re-invite (External RTP bridge)");
 	}
 
+	if (ast_test_flag(&p->flags[1], SIP_SENDRPID))
+		add_rpid(&req, p);
+
 	if (p->do_history)
 		append_history(p, "ReInv", "Re-invite sent");
+
 	try_suggested_sip_codec(p);
 	if (t38version)
 		add_sdp(&req, p, oldsdp, FALSE, TRUE);
@@ -10266,7 +10362,9 @@
 
 	if (!p->initreq.headers || init > 2)
 		initialize_initreq(p, &req);
-	p->lastinvite = p->ocseq;
+	if (sipmethod == SIP_INVITE) {
+		p->lastinvite = p->ocseq;
+	}
 	return send_request(p, &req, init ? XMIT_CRITICAL : XMIT_RELIABLE, p->ocseq);
 }
 
@@ -10819,11 +10917,15 @@
 			p->lastinvite = p->ocseq;
 			ast_set_flag(&p->flags[0], SIP_OUTGOING);
 			send_request(p, &req, XMIT_CRITICAL, p->ocseq);
-		} else {
+		} else if (is_method_allowed(&p->allowed_methods, SIP_UPDATE)) {
 			reqprep(&req, p, SIP_UPDATE, 0, 1);
 			add_rpid(&req, p);
+			add_header(&req, "X-Asterisk-rpid-update", "Yes");
 			add_header_contentLength(&req, 0);
 			send_request(p, &req, XMIT_CRITICAL, p->ocseq);
+		} else {
+			/* We cannot send the update yet, so we have to wait until we can */
+			ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
 		}
 	} else {
 		if (ast_test_flag(&p->flags[1], SIP_PAGE2_RPID_IMMEDIATE)) {
@@ -11428,6 +11530,7 @@
 	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
@@ -12434,6 +12537,7 @@
 	}
 
 	if (peer) {
+		ao2_lock(peer);
 		if (!peer->host_dynamic) {
 			ast_log(LOG_ERROR, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name);
 			res = AUTH_PEER_NOT_DYNAMIC;
@@ -12478,6 +12582,7 @@
 
 			} 
 		}
+		ao2_unlock(peer);
 	}
 	if (!peer && sip_cfg.autocreatepeer) {
 		/* Create peer if we have autocreate mode enabled */
@@ -12487,7 +12592,7 @@
 			if (peer->addr.sin_addr.s_addr) {
 				ao2_t_link(peers_by_ip, peer, "link peer into peers-by-ip table");
 			}
-			
+			ao2_lock(peer);
 			if (sip_cancel_destroy(p))
 				ast_log(LOG_WARNING, "Unable to cancel SIP destruction.  Expect bad things.\n");
 			switch (parse_register_contact(p, peer, req)) {
@@ -12510,6 +12615,7 @@
 				res = 0;
 				break;
 			}
+			ao2_unlock(peer);
 		}
 	}
 	if (!peer && sip_cfg.alwaysauthreject) {
@@ -12572,8 +12678,14 @@
 			break;
 		}
 	}
-	if (peer)
+	if (peer) {
+		ao2_lock(peer);
+		if (peer->allowed_methods == SIP_UNKNOWN) {
+			peer->allowed_methods = set_pvt_allowed_methods(p, req);
+		}
+		ao2_unlock(peer);
 		unref_peer(peer, "register_verify: unref_peer: tossing stack peer pointer at end of func");
+	}
 
 	return res;
 }
@@ -13552,6 +13664,11 @@
 	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;
+	}
 	if (peer->callingpres)	/* Peer calling pres setting will override RPID */
 		p->callingpres = peer->callingpres;
 	if (peer->maxms && peer->lastms)
@@ -17261,6 +17378,20 @@
 	return 0;
 }
 
+/*!
+ * \brief Handle authentication challenge for SIP UPDATE
+ *
+ * This function is only called upon the receipt of a 401/407 response to an UPDATE.
+ */
+static void handle_response_update(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno)
+{
+	if (p->options) {
+		p->options->auth_type = (resp == 401 ? WWW_AUTH : PROXY_AUTH);
+	}
+	if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, resp, SIP_UPDATE, 1)) {
+		ast_log(LOG_NOTICE, "Failed to authenticate on UPDATE to '%s'\n", get_header(&p->initreq, "From"));
+	}
+}
 
 /*! \brief Handle SIP response to INVITE dialogue */
 static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno)
@@ -17277,6 +17408,21 @@
 		ast_debug(4, "SIP response %d to RE-invite on %s call %s\n", resp, outgoing ? "outgoing" : "incoming", p->callid);
 	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);
@@ -17664,6 +17810,7 @@
 /* \brief Handle SIP response in SUBSCRIBE transaction */
 static void handle_response_subscribe(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno)
 {
+	struct sip_peer *peer;
 	if (!p->mwi) {
 		return;
 	}
@@ -17671,6 +17818,15 @@
 	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");
+		}
 		if (p->options) {
 			ast_free(p->options);
 			p->options = NULL;
@@ -17983,6 +18139,12 @@
 		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);
 	}
@@ -18024,6 +18186,7 @@
 	char *c_copy = ast_strdupa(c);
 	/* Skip the Cseq and its subsequent spaces */
 	const char *msg = ast_skip_blanks(ast_skip_nonblanks(c_copy));
+	struct sip_peer *peer;
 
 	if (!msg)
 		msg = "";
@@ -18124,7 +18287,9 @@
 				handle_response_subscribe(p, resp, rest, req, seqno);
 			else if (p->registry && sipmethod == SIP_REGISTER)
 				res = handle_response_register(p, resp, rest, req, seqno);
-			else if (sipmethod == SIP_BYE) {
+			else if (sipmethod == SIP_UPDATE) {
+				handle_response_update(p, resp, rest, req, seqno);
+			} else if (sipmethod == SIP_BYE) {
 				if (p->options)
 					p->options->auth_type = resp;
 				if (ast_strlen_zero(p->authname)) {
@@ -18224,6 +18389,11 @@
 			}
 			break;
 		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;
+				unref_peer(peer, "handle_response: marking a specific method as unallowed");
+			}
 			if (sipmethod == SIP_INVITE)
 				handle_response_invite(p, resp, rest, req, seqno);
 			else if (sipmethod == SIP_REFER)
@@ -19387,6 +19557,36 @@
 }
 
 /*!
+ * \brief bare-bones support for SIP UPDATE
+ *
+ * XXX This is not even close to being RFC 3311-compliant. We don't advertise
+ * that we support the UPDATE method, so no one should ever try sending us
+ * an UPDATE anyway. However, Asterisk can send an UPDATE to change connected
+ * line information, so we need to be prepared to handle this. The way we distinguish
+ * such an UPDATE is through the X-Asterisk-rpid-update header.
+ *
+ * Actually updating the media session may be some future work.
+ */
+static int handle_request_update(struct sip_pvt *p, struct sip_request *req)
+{
+	if (ast_strlen_zero(get_header(req, "X-Asterisk-rpid-update"))) {
+		transmit_response(p, "501 Method Not Implemented", req);
+		return 0;
+	}
+	if (get_rpid(p, req)) {
+		struct ast_party_connected_line connected;
+		ast_party_connected_line_init(&connected);
+		connected.id.number = (char *) p->cid_num;
+		connected.id.name = (char *) p->cid_name;
+		connected.id.number_presentation = p->callingpres;
+		connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
+		ast_channel_queue_connected_line_update(p->owner, &connected);
+	}
+	transmit_response(p, "200 OK", req);
+	return 0;
+}
+
+/*!
  * \brief Handle incoming INVITE request
  * \note If the INVITE has a Replaces header, it is part of an
  *	attended transfer. If so, we do not go through the dial
@@ -21454,6 +21654,9 @@
 	case SIP_NOTIFY:
 		res = handle_request_notify(p, req, sin, seqno, e);
 		break;
+	case SIP_UPDATE:
+		res = handle_request_update(p, req);
+		break;
 	case SIP_ACK:
 		/* Make sure we don't ignore this */
 		if (seqno == p->pendinginvite) {
@@ -22910,6 +23113,9 @@
 		} else if (ast_true(v->value)) {
 			ast_set_flag(&flags[0], SIP_SENDRPID_RPID);
 		}
+	} else if (!strcasecmp(v->name, "rpid_update")) {
+		ast_set_flag(&mask[1], SIP_PAGE2_RPID_UPDATE);
+		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RPID_UPDATE);
 	} else if (!strcasecmp(v->name, "rpid_immediate")) {
 		ast_set_flag(&mask[1], SIP_PAGE2_RPID_IMMEDIATE);
 		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RPID_IMMEDIATE);

Modified: team/group/security_events/configs/sip.conf.sample
URL: http://svn.asterisk.org/svn-view/asterisk/team/group/security_events/configs/sip.conf.sample?view=diff&rev=195630&r1=195629&r2=195630
==============================================================================
--- team/group/security_events/configs/sip.conf.sample (original)
+++ team/group/security_events/configs/sip.conf.sample Tue May 19 17:12:09 2009
@@ -232,6 +232,13 @@
                                 ; This is identical to sendrpid=yes
 ;sendrpid = pai                 ; Use the "P-Asserted-Identity" header
                                 ; to send the identity of the remote party
+;rpid_update = no               ; In certain cases, the only method by which a connected line
+                                ; change may be immediately transmitted is with a SIP UPDATE request.
+                                ; If communicating with another Asterisk server, and you wish to be able
+                                ; transmit such UPDATE messages to it, then you must enable this option.
+                                ; Otherwise, we will have to wait until we can send a reinvite to
+                                ; transmit the information.
+
 ;progressinband=never           ; If we should generate in-band ringing always
                                 ; use 'never' to never use in-band signalling, even in cases
                                 ; where some buggy devices might not render it




More information about the svn-commits mailing list