Example #1
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;
	short info_dirty = 0;

	/*
	 * 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)
		return;

	sst_msg_info_t minfo = {0,0,0,0};
	sst_info_t *info = (sst_info_t *)*(params->param);
	sst_info_t tmp_info;

	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 */
		if (info->interval < minfo.min_se)
			CHECK_AND_UPDATE_SST_INFO(info, interval, minfo.min_se, info_dirty);

		goto update_info; /* 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) {
			CHECK_AND_UPDATE_SST_INFO_TMP(info, supported,
					(minfo.supported?SST_UAS:SST_UNDF),info_dirty, tmp_info);
		}
		if (minfo.se != 0) {
			if (sst_interval > minfo.min_se)
				CHECK_AND_UPDATE_SST_INFO(info, interval, sst_interval, info_dirty);
			else
				CHECK_AND_UPDATE_SST_INFO_TMP(info, interval,
						MAX(minfo.se, sst_min_se), info_dirty, tmp_info);
			LM_DBG("UAS supports timer\n");
			set_dialog_lifetime(did, info->interval);
		}
		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)
					CHECK_AND_UPDATE_SST_INFO(info, interval, sst_interval,
							info_dirty);
				else
					CHECK_AND_UPDATE_SST_INFO_TMP(info, interval,
						MAX(minfo.se, sst_min_se), info_dirty, tmp_info);
				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 */
				set_dialog_lifetime(did, info->interval);
			}
			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);
				CHECK_AND_UPDATE_SST_INFO_TMP(info, interval,
						param?*param:12*3600, info_dirty, tmp_info);
				set_dialog_lifetime(did, info->interval);
			}
		}
	} /* End of 2XX for an INVITE */

update_info:
	if (info_dirty){
		str raw_info = {(char*)info, sizeof(sst_info_t)};
		if (dlg_binds->store_dlg_value(did, &info_val_name, &raw_info) != 0) {
			LM_ERR("sst_info can't be updated\n");
		}
	}
}
Example #2
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! */
		}
	}
	/* We keep the sst_info in the dialog's vals in case of restarting */
	/* No const here because of store_dlg_value's definition */
	str raw_info = {(char*)info, sizeof(sst_info_t)};
	if (dlg_binds->store_dlg_value(did, &info_val_name, &raw_info) != 0) {
		LM_ERR("No sst_info can be added to the dialog."
				"This dialog won't be considered after restart!\n");
	}

	dlg_binds->set_mod_flag(did, SST_DIALOG_FLAG);

	setup_dialog_callbacks(did, info);
	/* Early setup of default timeout */
	set_dialog_lifetime(did, info->interval);
	return;
}
Example #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_info_t tmp_info;
	sst_msg_info_t minfo = {0,0,0,0};
	struct sip_msg* msg = params->msg;
	short info_dirty = 0;

	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)
					CHECK_AND_UPDATE_SST_INFO(info, interval, sst_interval, info_dirty);
				else
					CHECK_AND_UPDATE_SST_INFO_TMP(info, interval,
							MAX(minfo.se, sst_min_se), info_dirty, tmp_info);
			}
			CHECK_AND_UPDATE_SST_INFO_TMP(info, supported,
					(minfo.supported?SST_UAC:SST_UNDF), info_dirty, tmp_info);
			set_dialog_lifetime(did, 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_dialog_lifetime(did, 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_dialog_lifetime(did, minfo.se);
			CHECK_AND_UPDATE_SST_INFO_TMP(info, supported,
					(minfo.supported?SST_UAC:SST_UNDF), info_dirty, tmp_info);
			CHECK_AND_UPDATE_SST_INFO(info, interval, minfo.se, info_dirty);
		}
	}

	if (info_dirty){
		str raw_info = {(char*)info, sizeof(sst_info_t)};
		if (dlg_binds->store_dlg_value(did, &info_val_name, &raw_info) != 0) {
			LM_ERR("sst_info can't be updated\n");
		}
	}
}
Example #4
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_dialog_lifetime(did, 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_dialog_lifetime(did, 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_dialog_lifetime(did, minfo.se);
	info->supported = (minfo.supported?SST_UAC:SST_UNDF);
			info->interval = minfo.se;
		}
	}
}