Exemplo n.º 1
0
/**
 * Callback from the dialog module when the dialog is being updated in
 * its life span. We are only interested in the INVITE or UPDATE if
 * SST is supported and active for this dialog. In this case, we need
 * to update the expire time for the dialog based on the
 * Session-Expires: header in the reINVITE/UPDATE request.
 *
 * When this callback returns control to the dialog module it WILL
 * reset the timeout of the dialog. We need to make sure we set the
 * AVP here or the dialog timeout will be reset to the DEFAULT value
 * if this is a different transaction. (so the AVP value is gone)
 *
 * @param did - The dialog structure. The pointer is used as an ID.
 * @param type - The reason for the callback. DLGCB_REQ_WITHIN
 * @param params - The sst information
 */
static void sst_dialog_request_within_CB(struct dlg_cell* did, int type,
		struct dlg_cb_params * params)
{
	sst_info_t *info = (sst_info_t *)*(params->param);
	sst_msg_info_t minfo = {0,0,0,0};
	struct sip_msg* msg = params->req;

	if (msg->first_line.type == SIP_REQUEST) {
		if ((msg->first_line.u.request.method_value == METHOD_INVITE ||
						msg->first_line.u.request.method_value == METHOD_UPDATE)) {

			LM_DBG("Update by a REQUEST. %.*s\n", 
					msg->first_line.u.request.method.len, 
					msg->first_line.u.request.method.s);
			if (parse_msg_for_sst_info(msg, &minfo)) {
				LM_ERR("failed to parse sst information\n"); 
				return;
			}
			/* Early resetting of the value here */
			set_timeout_avp(msg, minfo.se);
			info->interval = minfo.se;
		}
		else if (msg->first_line.u.request.method_value == METHOD_PRACK) {
			/* Special case here. The PRACK will cause the dialog
			 * module to reset the timeout value to the ldg->lifetime
			 * value and look for the new AVP value bound to the
			 * 1XX/PRACK/200OK/ACK transaction and not to the
			 * INVITE/200OK avp value. So we need to set the AVP
			 * again! I think this is a bug in the dialog module,
			 * either it should ignore PRACK like it ignored ACK, or
			 * the setting of the timeout value when returning to the
			 * confiremed callback code should look for the new AVP
			 * value, which is does not.
			 */
			LM_DBG("PRACK workaround applied!\n");
			set_timeout_avp(msg, info->interval);
		}
	}
	else if (msg->first_line.type == SIP_REPLY) {
		if ((msg->first_line.u.reply.statuscode > 199 &&
						msg->first_line.u.reply.statuscode < 300)) {
			/*
			 * To spec (RFC) the internal time out value so not be reset
			 * until here.
			 */
			LM_DBG("Update by a REPLY %d %.*s\n", 
					msg->first_line.u.reply.statuscode,
					msg->first_line.u.reply.reason.len, 
					msg->first_line.u.reply.reason.s);
			if (parse_msg_for_sst_info(msg, &minfo)) {
				LM_ERR("failed to parse sst information\n");
				return;
			}
			set_timeout_avp(msg, minfo.se);
			info->interval = minfo.se;
		}
	}
}
Exemplo n.º 2
0
/**
 * This callback is called on any response message in the lifespan of
 * the dialog. The callback is called just before the message is
 * copied to pkg memory so it is still mutable.
 *
 * @param did - The dialog structure. The pointer is used as an ID.
 * @param type - The reason for the callback. DLGCB_CONFIRMED
 * @param params - The sst information
 */
static void sst_dialog_response_fwded_CB(struct dlg_cell* did, int type,
		struct dlg_cb_params * params) 
{
	struct sip_msg* msg = params->msg;
	int *param;

	/*
	 * This test to see if the message is a response sould ALWAYS be
	 * true. This callback should not get called for requests. But
	 * lets be safe.
	 */
	if (msg->first_line.type == SIP_REPLY) {
		sst_msg_info_t minfo = {0,0,0,0};
		sst_info_t *info = (sst_info_t *)*(params->param);

		LM_DBG("Dialog seen REPLY %d %.*s\n", 
				msg->first_line.u.reply.statuscode,
				msg->first_line.u.reply.reason.len, 
				msg->first_line.u.reply.reason.s);
		/*
		 * Need to check to see if it is a 422 response. If it is,
		 * make sure our Min-SE: for this dialog is set at least as
		 * large as in the Min-SE: in the reply 422 message. If not,
		 * we will create an INVITE, 422 loop.
		 */
		if (msg->first_line.u.reply.statuscode == 422) {
			if (parse_msg_for_sst_info(msg, &minfo)) {
				LM_ERR("failed to prase sst information for thr 422 reply\n");
				return;
			}
			/* Make sure we do not try to use anything smaller */
			info->interval = MAX(info->interval, minfo.min_se);
			return; /* There is nothing else to do with this */
		}
		/*
		 * We need to get the method this reply is for from the CSEQ
		 * body. The RFC states we can only play with 2XX from the
		 * INVITE or reINVTE/UPDATE.
		 */
		if (!msg->cseq && ((parse_headers(msg, HDR_CSEQ_F, 0) == -1) || !msg->cseq)) {
			LM_ERR("failed to parse CSeq\n");
			return;
		}
		
		/* 2XX replies to INVITES only !*/
		if (msg->first_line.u.reply.statuscode > 199 &&
				msg->first_line.u.reply.statuscode < 300 &&
				(get_cseq(msg)->method_id == METHOD_INVITE ||
						get_cseq(msg)->method_id == METHOD_UPDATE)) {
			if (parse_msg_for_sst_info(msg, &minfo)) {
				LM_ERR("failed to parse sst information for the 2XX reply\n");
				return;
			}
			LM_DBG("parsing 200 OK response %d / %d\n", minfo.supported, minfo.se);
			if (info->supported != SST_UAC) {
				info->supported = (minfo.supported?SST_UAS:SST_UNDF);
			}
			if (minfo.se != 0) {
				if (sst_interval > minfo.min_se)
					info->interval = sst_interval;
				else
					info->interval = MAX(minfo.se, sst_min_se);
				LM_DBG("UAS supports timer\n");
				if (set_timeout_avp(msg, info->interval)) {
					// FIXME: need an error message here
					return;
				}
			}
			else {
				/* no se header found, we want to resquest it. */
				if (info->supported == SST_UAC) {
					char se_buf[80];
					
					LM_DBG("UAC supports timer\n");
					LM_DBG("appending the Session-Expires: header to the 2XX reply."
							" UAC will deal with it.\n");
					/*
					 * GOOD! we can just insert the Session-Expires:
					 * header and forward back to the UAC and it will
					 * deal with refreshing the session.
					 */
					if (sst_interval > minfo.min_se)
						info->interval = sst_interval;
					else
						info->interval = MAX(minfo.se, sst_min_se);
					snprintf(se_buf, 80, "Session-Expires: %d;refresher=uac\r\n", 
							info->interval);
					if (append_header(msg, se_buf)) {
						LM_ERR("failed to append Session-Expires header\n");
						return;
					}
					/* Set the dialog timeout HERE */
					if (set_timeout_avp(msg, info->interval)) {
						return;
					}
				}
				else {
					/* We are sunk, uac did not request it, and it
					 * does not support it */
					LM_DBG("UAC and UAS do not support timers!"
							" No session timers for this session.\n");
					param = find_param_export("dialog", "default_timeout", INT_PARAM);
					info->interval = param?*param:12*3600;
					if (set_timeout_avp(msg, info->interval)) {
						return;
					}
				}
			}
		} /* End of 2XX for an INVITE */
	} /* If the msg is a repsonse and not a request */
}
Exemplo n.º 3
0
/**
 * Callback from the dialog module when the dialog is being updated in
 * its life span. We are only interested in the INVITE or UPDATE if
 * SST is supported and active for this dialog. In this case, we need
 * to update the expire time for the dialog based on the
 * Session-Expires: header in the reINVITE/UPDATE request.
 *
 * When this callback returns control to the dialog module it WILL
 * reset the timeout of the dialog. We need to make sure we set the
 * AVP here or the dialog timeout will be reset to the DEFAULT value
 * if this is a different transaction. (so the AVP value is gone)
 *
 * @param did - The dialog structure. The pointer is used as an ID.
 * @param type - The reason for the callback. DLGCB_REQ_WITHIN
 * @param params - The sst information
 */
static void sst_dialog_request_within_CB(struct dlg_cell* did, int type,
		struct dlg_cb_params * params)
{
	sst_info_t *info = (sst_info_t *)*(params->param);
	sst_msg_info_t minfo = {0,0,0,0};
	struct sip_msg* msg = params->msg;

	if (msg->first_line.type == SIP_REQUEST) {
		if ((msg->first_line.u.request.method_value == METHOD_INVITE ||
						msg->first_line.u.request.method_value == METHOD_UPDATE)) {

			LM_DBG("Update by a REQUEST. %.*s\n", 
					msg->first_line.u.request.method.len, 
					msg->first_line.u.request.method.s);
			if (parse_msg_for_sst_info(msg, &minfo)) {
				// FIXME: need an error message here
				return;
			}
			/* Early resetting of the value here */
			if (minfo.se > 0) {
				if (sst_interval > minfo.min_se)
					info->interval = sst_interval;
				else
					info->interval = MAX(minfo.se, sst_min_se);
			}
			info->supported = (minfo.supported?SST_UAC:SST_UNDF);
			set_timeout_avp(msg, info->interval);
		}
		else if (msg->first_line.u.request.method_value == METHOD_PRACK
		|| msg->first_line.u.request.method_value == METHOD_ACK) {
			/* Special case here. The PRACK will cause the dialog
			 * module to reset the timeout value to the ldg->lifetime
			 * value and look for the new AVP value bound to the
			 * 1XX/PRACK/200OK/ACK transaction and not to the
			 * INVITE/200OK avp value. So we need to set the AVP
			 * again!
			 */
			LM_DBG("ACK/PRACK workaround applied!%d\n", info->interval);
			set_timeout_avp(msg, info->interval);
		}
	}
	else if (msg->first_line.type == SIP_REPLY) {
		if ((msg->first_line.u.reply.statuscode > 199 &&
						msg->first_line.u.reply.statuscode < 300)) {
			/*
			 * To spec (RFC) the internal time out value so not be reset
			 * until here.
			 */
			LM_DBG("Update by a REPLY %d %.*s\n", 
					msg->first_line.u.reply.statuscode,
					msg->first_line.u.reply.reason.len, 
					msg->first_line.u.reply.reason.s);
			if (parse_msg_for_sst_info(msg, &minfo)) {
				// FIXME: need an error message here
				return;
			}
			set_timeout_avp(msg, minfo.se);
	info->supported = (minfo.supported?SST_UAC:SST_UNDF);
			info->interval = minfo.se;
		}
	}
}
Exemplo n.º 4
0
/**
 * Every time a new dialog is created (from a new INVITE) the dialog
 * module will call this callback function. We need to track the
 * dialogs lifespan from this point forward until it is terminated
 * with a BYE, CANCEL, etc. In the process, we will see if either or
 * both ends of the conversation supports SIP Session Timers and setup
 * the dialog timeout to expire at the session timer expire time. Each
 * time the new re-INVITE is seen to update the SST, we will reset the
 * life span of the dialog to match it.
 *
 * This function will setup the other types of dialog callbacks
 * required to track the lifespan of the dialog. It will also start
 * the state tracking to figure out if and who supports SST.
 *
 * As per RFC4028: Request handling:
 * 
 * - The proxy may insert a SE header if none found.
 * - The SE value can be anything >= Min-SE (if found)
 * - The proxy MUST NOT add a refresher parameter to the SE.
 *
 * - If SE is already there, the Proxy can reduce its value but no
 *   lower then the Min-SE value if present.
 * - If the SE value is >= Min-SE the proxy MUST NOT increase it!
 * - If the SE value is < Min-SE (settable by the proxy) the proxy
 *   MUST increase the SE value to >= the new Min-SE.
 * - The proxy MUST NOT insert or change the refresher parameter.
 *
 * - If the supported=timer is found, the proxy may reject the request
 *   with a 422 if the SE value is smaller then the local policy. The
 *   422 MUST hold the proxies Min-SE value >= 90.
 * - If support=timer is NOT indecated, the proxy can't reject with a
 *   422 but can include/increase the MIN-SE: to be = to local policy.
 *   and increase the SE to match the new Min-SE value.
 * - the proxy MUST NOT insert/change the Min-SE header if
 *   supported=timer is present. (DoS attacks)
 *
 * @param did - The dialog ID
 * @param type - The trigger event type (CREATED)
 * @param params - The pointer to nothing. As we did not attach
 *                anything to this callback in the dialog module.
 */
void sst_dialog_created_CB(struct dlg_cell *did, int type,
		struct dlg_cb_params * params)
{
	sst_info_t *info = NULL;
	sst_msg_info_t minfo;
	struct sip_msg* msg = params->msg;

	memset(&minfo, 0, sizeof(sst_msg_info_t));
	/*
	 * Only deal with messages flaged as SST interested.
	 */
	if ((msg->flags & sst_flag) != sst_flag) {
		LM_DBG("SST flag was not set for this request\n");
		return;
	}

	/* 
	 * look only at INVITE
	 */
	if (msg->first_line.type != SIP_REQUEST ||
			msg->first_line.u.request.method_value != METHOD_INVITE) {
		LM_WARN("dialog create callback called with a non-INVITE request.\n");
		return;
	}

	/*
	 * Gather all he information about SST for this message
	 */
	if (parse_msg_for_sst_info(msg, &minfo)) {
		LM_ERR("failed to parse sst information\n");
		return;
	}

	info = (sst_info_t *)shm_malloc(sizeof(sst_info_t));
	memset(info, 0, sizeof(sst_info_t));
	info->requester = (minfo.se?SST_UAC:SST_UNDF);
	info->supported = (minfo.supported?SST_UAC:SST_UNDF);
	info->interval = MAX(sst_interval, 90); /* For now, will set for real
										  * later */

	if (minfo.se != 0) {
		/* 
		 * There is a SE already there, this is good, we just need to
		 * check the values out a little before passing it along.
		 */
		if (minfo.se < sst_min_se) {
			/* 
			 * Problem, the requested Session-Expires is too small for
			 * our local policy. We need to fix it, or reject it or
			 * ignore it.
			 */
			if (!minfo.supported) {
				/* 
				 * Increase the Min-SE: value in the request and
				 * forward it.
				 */
				char buf[80];
				if (minfo.min_se) {
					/* We need to update, which means, remove +
					 * insert */
					remove_minse_header(msg);
				}
				info->interval = MAX(sst_min_se, minfo.min_se);
				snprintf(buf, 80, "Min-SE: %d\r\n", info->interval);
				if (append_header(msg, buf)) {
					LM_ERR("Could not append modified Min-SE: header\n");
				}
			}
			else if (sst_reject) {
				/* Make sure that that all are at least 90 */
				send_reject(msg, MAX(MAX(sst_min_se, minfo.min_se), 90));
				shm_free(info);
				return;
			}
		}  /* end of se < sst_min_se */
		else {
			/* Use the INVITE SE: value */
			info->interval = minfo.se;
		}
	}
	else {
		/* 
		 * No Session-Expire: stated in request.
		 */
		char buf[80];

		info->interval = MAX(minfo.min_se, sst_min_se);

		if (minfo.min_se && minfo.min_se < sst_min_se) {
			remove_minse_header(msg);
			snprintf(buf, 80, "Min-SE: %d\r\n", info->interval);
			if (append_header(msg, buf)) {
				LM_ERR("failed to append modified Min-SE: header\n");
				/* What to do? Let is slide, we can still work */
			}
		}
		
		info->interval = MAX(info->interval, sst_interval);
		info->requester = SST_PXY;
		snprintf(buf, 80, "Session-Expires: %d\r\n", info->interval);
		if (append_header(msg, buf)) {
			LM_ERR("failed to append Session-Expires header to proxy "
					"requested SST.\n");
			shm_free(info);
			return; /* Nothing we can do! */
		}
	}
	setup_dialog_callbacks(did, info);
	/* Early setup of default timeout */
	set_timeout_avp(msg, info->interval);
	return;
}