mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
10 KiB
290 lines
10 KiB
/*
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2001-2003 FhG Fokus
|
|
*
|
|
* This file is part of ser, a free SIP server.
|
|
*
|
|
* ser is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* For a license to use the ser software under conditions
|
|
* other than those described here, or to purchase support for this
|
|
* software, please contact iptel.org by e-mail at the following addresses:
|
|
* info@iptel.org
|
|
*
|
|
* ser 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include "select.h"
|
|
#include "h_table.h"
|
|
#include "t_lookup.h"
|
|
#include "../../dprint.h"
|
|
#include "../../mem/mem.h"
|
|
#include "../../ut.h"
|
|
#include "../../select.h"
|
|
#include "../../select_buf.h"
|
|
#include "../../parser/msg_parser.h"
|
|
|
|
#define RETURN0_res(x) {*res=(x);return 0;}
|
|
|
|
inline static int select_tm_get_cell(struct sip_msg* msg, int *branch, struct cell **t) {
|
|
|
|
/* make sure we know the associated transaction ... */
|
|
if (t_check( msg , branch )==-1) /* it's not necessary whan calling from script because already done */
|
|
return -1;
|
|
|
|
/*... if there is none, tell the core router to fwd statelessly */
|
|
*t = get_t();
|
|
if ( (*t==0)||(*t==T_UNDEFINED)) return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int select_tm(str* res, select_t* s, struct sip_msg* msg) {
|
|
int branch;
|
|
struct cell *t;
|
|
if (select_tm_get_cell(msg, &branch, &t) < 0) {
|
|
res->s = "0";
|
|
}
|
|
else {
|
|
res->s = "1";
|
|
}
|
|
res->len = 1;
|
|
return 0;
|
|
}
|
|
|
|
#define SEL_BRANCH_POS 2
|
|
#define BRANCH_NO(_s_) (_s_->params[SEL_BRANCH_POS].v.i)
|
|
|
|
#define SELECT_check(msg) \
|
|
int branch; \
|
|
struct cell *t; \
|
|
if (select_tm_get_cell(msg, &branch, &t) < 0) return -1;
|
|
|
|
#define SELECT_check_branch(_s_, _msg_) \
|
|
SELECT_check(_msg_); \
|
|
if (BRANCH_NO(_s_) >=t->nr_of_outgoings) return -1;
|
|
|
|
|
|
/* string resides in shared memory but I think it's not worth copying to
|
|
* static buffer (str_to_static_buffer) as minimal probability that string
|
|
* is changed by other process (or cell is already locked ?)
|
|
*/
|
|
|
|
static int select_tm_method(str* res, select_t* s, struct sip_msg* msg) {
|
|
SELECT_check(msg);
|
|
RETURN0_res(t->method);
|
|
}
|
|
|
|
static ABSTRACT_F(select_tm_uas);
|
|
|
|
static int select_tm_uas_status(str* res, select_t* s, struct sip_msg* msg) {
|
|
SELECT_check(msg);
|
|
return int_to_static_buffer(res, t->uas.status);
|
|
}
|
|
|
|
/* transaction cell has request in sip_msg structure which brings idea to
|
|
* use selects from select_core.c (e.g. to avoid copy&paste automatically
|
|
* generate select definitions in tm_init_selects()). But it's not so easy
|
|
* as select may perform any parsing which is stored in sip_msg structure.
|
|
* But transaction cell resides in shared memory while parsing is done in
|
|
* private memory. Therefore we support currently only complete request */
|
|
|
|
static int select_tm_uas_request(str* res, select_t* s, struct sip_msg* msg) {
|
|
SELECT_check(msg);
|
|
res->s = t->uas.request->buf;
|
|
res->len = t->uas.request->len;
|
|
return 0;
|
|
}
|
|
|
|
static int select_tm_uas_local_to_tag(str* res, select_t* s, struct sip_msg* msg) {
|
|
SELECT_check(msg);
|
|
RETURN0_res(t->uas.local_totag);
|
|
}
|
|
|
|
static int select_tm_uas_response(str* res, select_t* s, struct sip_msg* msg) {
|
|
SELECT_check(msg);
|
|
res->s = t->uas.response.buffer;
|
|
res->len = t->uas.response.buffer_len;
|
|
return 0;
|
|
}
|
|
|
|
/* TODO: implement a general select function that works with any
|
|
* kind of requests not only with negative ACKs
|
|
*/
|
|
static int select_tm_uas_request_neg_ack_retransmission(str* res, select_t* s, struct sip_msg* msg) {
|
|
int rv;
|
|
|
|
SELECT_check(msg);
|
|
rv = ((msg->REQ_METHOD == METHOD_ACK)
|
|
&& (t->uas.status >= 300)
|
|
/* Misuse the timer flag of the 200 retransmission buffer
|
|
* to check whether or not this is an ACK retransmission.
|
|
* Warning: this check is not 100% fail-safe because two
|
|
* ACKs can be processed in parallel and none of them
|
|
* may be considered a retransmission - Miklos */
|
|
&& (t->uas.response.t_active == 0)) ? 1 : -1;
|
|
|
|
return int_to_static_buffer(res, rv);
|
|
}
|
|
|
|
|
|
static ABSTRACT_F(select_tm_uac);
|
|
|
|
static int select_tm_uac_count(str* res, select_t* s, struct sip_msg* msg) {
|
|
SELECT_check(msg);
|
|
return int_to_static_buffer(res, t->nr_of_outgoings);
|
|
}
|
|
|
|
static int select_tm_uac_relayed(str* res, select_t* s, struct sip_msg* msg) {
|
|
SELECT_check(msg);
|
|
return int_to_static_buffer(res, t->relayed_reply_branch);
|
|
}
|
|
|
|
static ABSTRACT_F(select_tm_uac_branch);
|
|
|
|
/**
|
|
* Get last_status from current branch. Helper function.
|
|
* @see selects select_tm_uac_last_status
|
|
* @see select_tm_uac_response_retransmission
|
|
*/
|
|
static int get_last_status(struct sip_msg* msg, int *last_status)
|
|
{
|
|
unsigned int branch;
|
|
char *bptr;
|
|
int blen;
|
|
struct cell *t;
|
|
|
|
/* DBG("select_tm_uac_last_status: branch param name: '%.*s', value: '%.*s'\n",
|
|
msg->via1->branch->name.len,
|
|
msg->via1->branch->name.s,
|
|
msg->via1->branch->value.len,
|
|
msg->via1->branch->value.s);
|
|
*/
|
|
|
|
/* branch ID consist of MAGIC '.' HASHID '.' BRANCH_ID */
|
|
blen = 0;
|
|
for (bptr = msg->via1->branch->value.s + msg->via1->branch->value.len - 1;
|
|
bptr != msg->via1->branch->value.s;
|
|
bptr--, blen++)
|
|
{
|
|
if (*bptr == '.') break;
|
|
}
|
|
bptr++;
|
|
/* we have a pointer to the branch number */
|
|
/* DBG("branch number: '%.*s'\n", blen, bptr); */
|
|
if (reverse_hex2int(bptr, blen, &branch) < 0) {
|
|
ERR("Wrong branch number in Via1 branch param\n");
|
|
return -1;
|
|
}
|
|
|
|
t = get_t();
|
|
if ( (t == NULL) || (t == T_UNDEFINED) ) {
|
|
ERR("get_last_status: no transaction\n");
|
|
return -1;
|
|
}
|
|
|
|
/* DBG("select_tm_uac_last_status: branch = %d\n", branch); */
|
|
*last_status = t->uac[branch].last_received;
|
|
return 1;
|
|
}
|
|
/**
|
|
* Get last status in current branch.
|
|
*/
|
|
static int select_tm_uac_last_status(str* res, select_t* s, struct sip_msg* msg) {
|
|
int last_status;
|
|
if (get_last_status(msg, &last_status) < 0) return -1;
|
|
return int_to_static_buffer(res, last_status);
|
|
}
|
|
/**
|
|
* Comparison function which compares current status and last status
|
|
* in current branch.
|
|
* It is a simplified method how to detect incoming retransmited responses.
|
|
* @return 1 if @status is less or equal @tm.uac.last_status (retransmited response)
|
|
* otherwise returns -1 (not retransmited response).
|
|
* @see get_last_status
|
|
*/
|
|
static int select_tm_uac_response_retransmission(str* res, select_t* s, struct sip_msg* msg) {
|
|
int last_status, rv;
|
|
if (get_last_status(msg, &last_status) < 0) return -1;
|
|
rv = msg->first_line.u.reply.statuscode <= last_status ? 1 : -1;
|
|
|
|
/* DBG("select_tm_uac_response_retransmission: %d\n", rv); */
|
|
return int_to_static_buffer(res, rv);
|
|
}
|
|
|
|
static int select_tm_uac_status(str* res, select_t* s, struct sip_msg* msg) {
|
|
SELECT_check_branch(s, msg);
|
|
return int_to_static_buffer(res, t->uac[BRANCH_NO(s)].last_received);
|
|
}
|
|
|
|
static int select_tm_uac_uri(str* res, select_t* s, struct sip_msg* msg) {
|
|
SELECT_check_branch(s, msg);
|
|
RETURN0_res(t->uac[BRANCH_NO(s)].uri);
|
|
}
|
|
|
|
/* see select_tm_uas_request comments */
|
|
static int select_tm_uac_response(str* res, select_t* s, struct sip_msg* msg) { // struct
|
|
SELECT_check_branch(s, msg);
|
|
if (t->uac[BRANCH_NO(s)].reply) {
|
|
res->s = t->uac[BRANCH_NO(s)].reply->buf;
|
|
res->len = t->uac[BRANCH_NO(s)].reply->len;
|
|
return 0;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static int select_tm_uac_branch_request(str* res, select_t* s, struct sip_msg* msg) {
|
|
SELECT_check_branch(s, msg);
|
|
res->s = t->uac[BRANCH_NO(s)].request.buffer;
|
|
res->len = t->uac[BRANCH_NO(s)].request.buffer_len;
|
|
return 0;
|
|
}
|
|
|
|
static select_row_t select_declaration[] = {
|
|
{ NULL, SEL_PARAM_STR, STR_STATIC_INIT("tm"), select_tm, 0},
|
|
{ select_tm, SEL_PARAM_STR, STR_STATIC_INIT("method"), select_tm_method, 0},
|
|
{ select_tm, SEL_PARAM_STR, STR_STATIC_INIT("uas"), select_tm_uas, SEL_PARAM_EXPECTED},
|
|
{ select_tm_uas, SEL_PARAM_STR, STR_STATIC_INIT("status"), select_tm_uas_status, 0},
|
|
{ select_tm_uas, SEL_PARAM_STR, STR_STATIC_INIT("request"), select_tm_uas_request, 0},
|
|
{ select_tm_uas, SEL_PARAM_STR, STR_STATIC_INIT("req"), select_tm_uas_request, 0},
|
|
{ select_tm_uas, SEL_PARAM_STR, STR_STATIC_INIT("local_to_tag"), select_tm_uas_local_to_tag, 0},
|
|
{ select_tm_uas, SEL_PARAM_STR, STR_STATIC_INIT("response"), select_tm_uas_response, 0},
|
|
{ select_tm_uas, SEL_PARAM_STR, STR_STATIC_INIT("resp"), select_tm_uas_response, 0},
|
|
{ select_tm_uas_request, SEL_PARAM_STR, STR_STATIC_INIT("neg_ack_retransmission"), select_tm_uas_request_neg_ack_retransmission, 0},
|
|
|
|
{ select_tm, SEL_PARAM_STR, STR_STATIC_INIT("uac"), select_tm_uac, SEL_PARAM_EXPECTED},
|
|
{ select_tm_uac, SEL_PARAM_STR, STR_STATIC_INIT("count"), select_tm_uac_count, 0},
|
|
{ select_tm_uac, SEL_PARAM_STR, STR_STATIC_INIT("relayed"), select_tm_uac_relayed, 0},
|
|
{ select_tm_uac, SEL_PARAM_STR, STR_STATIC_INIT("last_status"), select_tm_uac_last_status, 0}, /* last status of current branch */
|
|
{ select_tm_uac, SEL_PARAM_STR, STR_STATIC_INIT("response_retransmission"), select_tm_uac_response_retransmission, 0}, /* last status of current branch */
|
|
{ select_tm_uac, SEL_PARAM_INT, STR_NULL, select_tm_uac_branch, SEL_PARAM_EXPECTED},
|
|
{ select_tm_uac_branch, SEL_PARAM_STR, STR_STATIC_INIT("status"), select_tm_uac_status, 0},
|
|
{ select_tm_uac_branch, SEL_PARAM_STR, STR_STATIC_INIT("uri"), select_tm_uac_uri, 0},
|
|
{ select_tm_uac_branch, SEL_PARAM_STR, STR_STATIC_INIT("response"), select_tm_uac_response, 0},
|
|
{ select_tm_uac_branch, SEL_PARAM_STR, STR_STATIC_INIT("resp"), select_tm_uac_response, 0},
|
|
{ select_tm_uac_branch, SEL_PARAM_STR, STR_STATIC_INIT("request"), select_tm_uac_branch_request, 0},
|
|
{ select_tm_uac_branch, SEL_PARAM_STR, STR_STATIC_INIT("req"), select_tm_uac_branch_request, 0},
|
|
|
|
{ NULL, SEL_PARAM_INT, STR_NULL, NULL, 0}
|
|
};
|
|
|
|
int tm_init_selects() {
|
|
register_select_table(select_declaration);
|
|
return 0;
|
|
}
|