freeswitch/libs/sofia-sip/libsofia-sip-ua/nta/nta_check.c

387 lines
11 KiB
C

/*
* This file is part of the Sofia-SIP package
*
* Copyright (C) 2005 Nokia Corporation.
*
* Contact: Pekka Pessi <pekka.pessi@nokia.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
/**@CFILE nta_check.c
* @brief Checks for features, MIME types, session timer.
*
* @author Pekka Pessi <Pekka.Pessi@nokia.com>
*
* @date Created: Wed Mar 8 16:35:05 EET 2006 ppessi
*/
#include "config.h"
#include <sofia-sip/su_tagarg.h>
#include <sofia-sip/sip_header.h>
#include <sofia-sip/sip_status.h>
#include <sofia-sip/sip_util.h>
#include <sofia-sip/nta.h>
/* ======================================================================== */
/* Request validation */
/**Check that we support all features which UAC requires.
*
* The list of supported features is compared with the list of features
* required by the UAC. If some features are not listed as supported, return
* 420. If @a irq is non-NULL, the 420 response message is sent to the
* client along with list of unsupported features in the @Unsupported
* header, too.
*
* @param irq incoming transaction object (may be NULL).
* @param sip contents of the SIP message
* @param supported list of protocol features supported
* @param tag, value, ... optional list of tagged arguments used
* when responding to the transaction
*
* @return 0 if successful.
* 420 if any of the required features is not supported.
*/
int nta_check_required(nta_incoming_t *irq,
sip_t const *sip,
sip_supported_t const *supported,
tag_type_t tag, tag_value_t value, ...)
{
int status = 0;
if (sip->sip_require) {
su_home_t home[SU_HOME_AUTO_SIZE(512)];
sip_unsupported_t *us;
su_home_auto(home, sizeof home);
us = sip_has_unsupported(home, supported, sip->sip_require);
if (us) {
status = 420;
if (irq) {
ta_list ta;
ta_start(ta, tag, value);
nta_incoming_treply(irq,
SIP_420_BAD_EXTENSION,
SIPTAG_UNSUPPORTED(us),
SIPTAG_SUPPORTED(supported),
ta_tags(ta));
ta_end(ta);
}
}
su_home_deinit(home);
}
return status;
}
/** Check that UAC supports all the required features.
*
* The list of required features is compared with the features supported by
* the UAC. If some features are not supported, return 421. If @a irq is
* non-NULL, the 421 response message is sent to the client, too.
*
* @param irq incoming transaction object (may be NULL).
* @param sip contents of the SIP message
* @param require list of required protocol features
* @param tag, value, ... optional list of tagged arguments used
* when responding to the transaction
*
* @return 0 if successful.
* 421 if any of the required features is not supported.
*/
int nta_check_supported(nta_incoming_t *irq,
sip_t const *sip,
sip_require_t *require,
tag_type_t tag, tag_value_t value, ...)
{
if (!sip_has_unsupported(NULL, sip->sip_supported, require))
return 0;
if (irq) {
ta_list ta;
ta_start(ta, tag, value);
nta_incoming_treply(irq,
SIP_421_EXTENSION_REQUIRED,
SIPTAG_REQUIRE(require),
ta_tags(ta));
ta_end(ta);
}
return 421;
}
/** Check that we allow the request method.
*
* The request-method is compared with the list of supported methods in @a
* allow. If match is found, 0 is is returned. Otherwise, if the
* request-method is well-known, 405 is returned. If the request-method is
* unknown, 501 is returned. If @a irq is non-NULL, the 405 or 501 response
* message is sent to the client, too.
*
* @param irq incoming transaction object (may be NULL).
* @param sip contents of the SIP message
* @param allow list of allowed methods
* @param tag, value, ... optional list of tagged arguments used
* when responding to the transaction
*
* @return 0 if successful, 405 is request-method is not allowed, 501 if
* request-method is unknown.
*/
int nta_check_method(nta_incoming_t *irq,
sip_t const *sip,
sip_allow_t const *allow,
tag_type_t tag, tag_value_t value, ...)
{
/* Check extensions */
sip_method_t method = sip->sip_request->rq_method;
char const *name = sip->sip_request->rq_method_name;
if (sip_is_allowed(allow, method, name))
return 0;
if (irq) {
ta_list ta;
ta_start(ta, tag, value);
if (method != sip_method_unknown)
/* Well-known method */
nta_incoming_treply(irq,
SIP_405_METHOD_NOT_ALLOWED,
SIPTAG_ALLOW(allow),
ta_tags(ta));
else
/* Completeley unknown method */
nta_incoming_treply(irq,
SIP_501_NOT_IMPLEMENTED,
SIPTAG_ALLOW(allow),
ta_tags(ta));
ta_end(ta);
}
return method != sip_method_unknown ? 405 : 501;
}
static char const application_sdp[] = "application/sdp";
/** Check that we understand session content in the request.
*
* If there is no @ContentDisposition header or the @ContentDisposition
* header indicated "session", the message body and content-type is compared
* with the acceptable session content-types listed in @a session_accepts.
* (typically, @c "application/sdp"). If no match is found, a 415 is
* returned. If @a irq is non-NULL, the 415 response message is sent to the
* client, too.
*
* If the @ContentDisposition header indicates something else but "session",
* and it does not contain "handling=optional" parameter, a 415 response is
* returned, too.
*
* Also, the @ContentEncoding header is checked. If it is not empty
* (indicating no content-encoding), a 415 response is returned, too.
*
* @param irq incoming (server) transaction object (may be NULL).
* @param sip contents of the SIP message
* @param session_accepts list of acceptable content-types for "session"
* content disposition
* @param tag, value, ... optional list of tagged arguments used
* when responding to the transaction
*
* @return 0 if successful, 415 if content-type is not acceptable.
*/
int nta_check_session_content(nta_incoming_t *irq,
sip_t const *sip,
sip_accept_t const *session_accepts,
tag_type_t tag, tag_value_t value, ...)
{
sip_content_type_t const *c = sip->sip_content_type;
sip_content_disposition_t const *cd = sip->sip_content_disposition;
int acceptable_type = 0, acceptable_encoding = 0;
if (sip->sip_payload == NULL)
return 0;
if (cd == NULL || su_casematch(cd->cd_type, "session")) {
sip_accept_t const *ab = session_accepts;
char const *c_type;
if (c)
c_type = c->c_type;
else if (sip->sip_payload->pl_len > 3 &&
su_casenmatch(sip->sip_payload->pl_data, "v=0", 3))
/* Missing Content-Type, but it looks like SDP */
c_type = application_sdp;
else
/* No chance */
ab = NULL, c_type = NULL;
for (; ab; ab = ab->ac_next) {
if (su_casematch(c_type, ab->ac_type))
break;
}
if (ab)
acceptable_type = 1;
}
else if (cd->cd_optional)
acceptable_type = 1;
/* Empty or missing Content-Encoding */
if (!sip->sip_content_encoding ||
!sip->sip_content_encoding->k_items ||
!sip->sip_content_encoding->k_items[0] ||
!sip->sip_content_encoding->k_items[0][0])
acceptable_encoding = 1;
if (acceptable_type && acceptable_encoding)
return 0;
if (irq) {
ta_list ta;
ta_start(ta, tag, value);
nta_incoming_treply(irq,
SIP_415_UNSUPPORTED_MEDIA,
SIPTAG_ACCEPT(session_accepts),
ta_tags(ta));
ta_end(ta);
}
return 415;
}
/**Check that UAC accepts one of listed MIME content-types.
*
* The list of acceptable content-types are compared with the acceptable
* content-types. If match is found, it is returned in @a return_acceptable.
* If no match is found, a 406 is returned. If @a irq is non-NULL, the 406
* response message is sent to the client, too.
*
* @param irq incoming transaction object (may be NULL).
* @param sip contents of the SIP message
* @param acceptable list of acceptable content types
* @param return_acceptable optional return-value parameter for
* matched content-type
* @param tag, value, ... optional list of tagged arguments used
* when responding to the transaction
*
* @return 406 if no content-type is acceptable by client, 0 if successful.
*/
int nta_check_accept(nta_incoming_t *irq,
sip_t const *sip,
sip_accept_t const *acceptable,
sip_accept_t const **return_acceptable,
tag_type_t tag, tag_value_t value, ...)
{
ta_list ta;
sip_accept_t const *ac, *ab;
sip_method_t method;
if (!acceptable)
return 0;
if (sip->sip_request)
method = sip->sip_request->rq_method;
else /* if (sip->sip_cseq) */
method = sip->sip_cseq->cs_method;
/* Missing Accept header implies support for SDP in INVITE and OPTIONS
* (and PRACK and UPDATE?)
*/
if (!sip->sip_accept && (method == sip_method_invite ||
method == sip_method_options ||
method == sip_method_prack ||
method == sip_method_update)) {
for (ab = acceptable; ab; ab = ab->ac_next)
if (su_casematch(application_sdp, ab->ac_type)) {
if (return_acceptable) *return_acceptable = ab;
return 0;
}
}
for (ac = sip->sip_accept; ac; ac = ac->ac_next) {
if (sip_q_value(ac->ac_q) == 0 || !ac->ac_type)
continue;
for (ab = acceptable; ab; ab = ab->ac_next)
if (su_casematch(ac->ac_type, ab->ac_type)) {
if (return_acceptable) *return_acceptable = ab;
return 0;
}
}
if (irq) {
ta_start(ta, tag, value);
nta_incoming_treply(irq,
SIP_406_NOT_ACCEPTABLE,
SIPTAG_ACCEPT(acceptable),
ta_tags(ta));
ta_end(ta);
}
return 406;
}
/**Check @SessionExpires header.
*
* If the proposed session-expiration time is smaller than @MinSE or our
* minimal session expiration time, respond with 422 containing shortest
* acceptable session expiration time in @MinSE header.
*
* @param irq incoming transaction object (may be NULL).
* @param sip contents of the SIP message
* @param my_min_se minimal session expiration time in seconds
* @param tag, value, ... optional list of tagged arguments used
* when responding to the transaction
*
* @return 422 if session expiration time is too small, 0 when successful.
*/
int nta_check_session_expires(nta_incoming_t *irq,
sip_t const *sip,
sip_time_t my_min_se,
tag_type_t tag, tag_value_t value, ...)
{
unsigned long min_se = my_min_se;
if (sip->sip_min_se && min_se < sip->sip_min_se->min_delta)
min_se = sip->sip_min_se->min_delta;
if (sip->sip_session_expires->x_delta >= min_se)
return 0;
if (irq) {
ta_list ta;
sip_min_se_t min_se0[1];
ta_start(ta, tag, value);
sip_min_se_init(min_se0)->min_delta = min_se;
nta_incoming_treply(irq,
SIP_422_SESSION_TIMER_TOO_SMALL,
SIPTAG_MIN_SE(min_se0),
ta_tags(ta));
ta_end(ta);
}
return 422;
}