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.
464 lines
12 KiB
464 lines
12 KiB
/*
|
|
* Serial forking functions
|
|
*
|
|
* Copyright (C) 2008 Juha Heinanen
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
* EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* History:
|
|
* -------
|
|
* 2008-10-22: Moved functions from lcr module to tm module (jh)
|
|
*/
|
|
|
|
#include "../../qvalue.h"
|
|
#include "../../mem/mem.h"
|
|
#include "../../socket_info.h"
|
|
#include "../../usr_avp.h"
|
|
#include "../../dset.h"
|
|
#include "../../parser/msg_parser.h"
|
|
#include "../../ut.h"
|
|
#include "config.h"
|
|
#include "t_funcs.h"
|
|
#include "t_lookup.h"
|
|
|
|
/* usr_avp flag for sequential forking */
|
|
#define Q_FLAG (1<<2)
|
|
|
|
/* Struture where information regarding contacts is stored */
|
|
struct contact {
|
|
str uri;
|
|
qvalue_t q;
|
|
str dst_uri;
|
|
str path;
|
|
unsigned int flags;
|
|
struct socket_info* sock;
|
|
unsigned short q_flag;
|
|
struct contact *next;
|
|
};
|
|
|
|
/*
|
|
* Frees contact list used by load_contacts function
|
|
*/
|
|
static inline void free_contact_list(struct contact *curr) {
|
|
struct contact *prev;
|
|
while (curr) {
|
|
prev = curr;
|
|
curr = curr->next;
|
|
pkg_free(prev);
|
|
}
|
|
}
|
|
|
|
/* Encode branch info from contact struct to str */
|
|
static inline int encode_branch_info(str *info, struct contact *con)
|
|
{
|
|
char *at, *s;
|
|
int len;
|
|
|
|
info->len = con->uri.len + con->dst_uri.len +
|
|
con->path.len + MAX_SOCKET_STR + INT2STR_MAX_LEN + 5;
|
|
info->s = pkg_malloc(info->len);
|
|
if (!info->s) {
|
|
LM_ERR("no memory left for branch info\n");
|
|
return 0;
|
|
}
|
|
at = info->s;
|
|
append_str(at, con->uri.s, con->uri.len);
|
|
append_chr(at, '\n');
|
|
append_str(at, con->dst_uri.s, con->dst_uri.len);
|
|
append_chr(at, '\n');
|
|
append_str(at, con->path.s, con->path.len);
|
|
append_chr(at, '\n');
|
|
if (con->sock) {
|
|
len = MAX_SOCKET_STR;
|
|
if (socket2str(at, &len, con->sock) < 0) {
|
|
LM_ERR("failed to convert socket to str\n");
|
|
return 0;
|
|
}
|
|
} else {
|
|
len = 0;
|
|
}
|
|
at = at + len;
|
|
append_chr(at, '\n');
|
|
s = int2str(con->flags, &len);
|
|
append_str(at, s, len);
|
|
append_chr(at, '\n');
|
|
info->len = at - info->s + 1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* Encode branch info from str */
|
|
static inline int decode_branch_info(char *info, str *uri, str *dst, str *path,
|
|
struct socket_info **sock,
|
|
unsigned int *flags)
|
|
{
|
|
str s, host;
|
|
int port, proto;
|
|
char *pos, *at, *tmp;
|
|
|
|
if (info == NULL) {
|
|
ERR("decode_branch_info: Invalid input string.\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Reset or return arguments to sane defaults */
|
|
uri->s = 0; uri->len = 0;
|
|
dst->s = 0; dst->len = 0;
|
|
path->s = 0; path->len = 0;
|
|
*sock = NULL;
|
|
*flags = 0;
|
|
|
|
/* Make sure that we have at least a non-empty URI string, it is fine if
|
|
* everything else is missing, but we need at least the URI. */
|
|
uri->s = info;
|
|
if ((pos = strchr(info, '\n')) == NULL) {
|
|
uri->len = strlen(info);
|
|
/* We don't even have the URI string, this is bad, report an
|
|
* error. */
|
|
if (uri->len == 0) goto uri_missing;
|
|
return 1;
|
|
}
|
|
uri->len = pos - info;
|
|
if (uri->len == 0) goto uri_missing;
|
|
|
|
/* If we get here we have at least the branch URI, now try to parse as
|
|
* much as you can. All output variable have been initialized above, so it
|
|
* is OK if any of the fields are missing from now on. */
|
|
dst->s = at = pos + 1;
|
|
if ((pos = strchr(at, '\n')) == NULL) {
|
|
dst->len = strlen(dst->s);
|
|
return 1;
|
|
}
|
|
dst->len = pos - at;
|
|
|
|
path->s = at = pos + 1;
|
|
if ((pos = strchr(at, '\n')) == NULL) {
|
|
path->len = strlen(path->s);
|
|
return 1;
|
|
}
|
|
path->len = pos - at;
|
|
|
|
s.s = at = pos + 1;
|
|
if ((pos = strchr(at, '\n')) == NULL) {
|
|
/* No LF found, that means we take the string till the final zero
|
|
* termination character and pass it directly to parse_phostport
|
|
* without making a zero-terminated copy. */
|
|
tmp = s.s;
|
|
s.len = strlen(s.s);
|
|
} else {
|
|
/* Our string is terminated by LF, so we need to make a
|
|
* zero-terminated copy of the string before we pass it to
|
|
* parse_phostport. */
|
|
s.len = pos - at;
|
|
if ((tmp = as_asciiz(&s)) == NULL) {
|
|
ERR("No memory left\n");
|
|
return 0;
|
|
}
|
|
}
|
|
if (s.len) {
|
|
if (parse_phostport(tmp, &host.s, &host.len,
|
|
&port, &proto) != 0) {
|
|
LM_ERR("parsing of socket info <%s> failed\n", tmp);
|
|
if (pos) pkg_free(tmp);
|
|
return 0;
|
|
}
|
|
|
|
*sock = grep_sock_info(&host, (unsigned short)port,
|
|
(unsigned short)proto);
|
|
if (*sock == 0) {
|
|
LM_ERR("invalid socket <%s>\n", tmp);
|
|
if (pos) pkg_free(tmp);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (pos) pkg_free(tmp);
|
|
else return 1;
|
|
|
|
s.s = at = pos + 1;
|
|
if ((pos = strchr(at, '\n')) == NULL) s.len = strlen(s.s);
|
|
else s.len = pos - s.s;
|
|
|
|
if (s.len) {
|
|
if (str2int(&s, flags) != 0) {
|
|
LM_ERR("failed to decode flags <%.*s>\n", STR_FMT(&s));
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
|
|
uri_missing:
|
|
ERR("decode_branch_info: Cannot decode branch URI.\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Loads contacts in destination set into contacts_avp in reverse
|
|
* priority order and associated each contact with Q_FLAG telling if
|
|
* contact is the last one in its priority class. Finally, removes
|
|
* all branches from destination set.
|
|
*/
|
|
int t_load_contacts(struct sip_msg* msg, char* key, char* value)
|
|
{
|
|
str uri, tmp, dst_uri, path, branch_info, *ruri;
|
|
qvalue_t first_q, q;
|
|
struct contact *contacts, *next, *prev, *curr;
|
|
int_str val;
|
|
int first_idx, idx;
|
|
struct socket_info* sock;
|
|
unsigned int flags;
|
|
|
|
/* Check if contacts_avp has been defined */
|
|
if (contacts_avp.n == 0) {
|
|
LM_ERR("feature has been disabled - "
|
|
"to enable define contacts_avp module parameter");
|
|
return -1;
|
|
}
|
|
|
|
/* Check if anything needs to be done */
|
|
if (nr_branches == 0) {
|
|
LM_DBG("t_load_contacts(): nothing to do - no branches!\n");
|
|
return 1;
|
|
}
|
|
|
|
ruri = (str *)0;
|
|
if (ruri_is_new) {
|
|
/* Take first q from Request-URI */
|
|
ruri = GET_RURI(msg);
|
|
if (!ruri) {
|
|
LM_ERR("no Request-URI found\n");
|
|
return -1;
|
|
}
|
|
first_q = get_ruri_q();
|
|
first_idx = 0;
|
|
} else {
|
|
/* Take first q from first branch */
|
|
uri.s = get_branch(0, &uri.len, &first_q, &dst_uri, &path, &flags,
|
|
&sock);
|
|
first_idx = 1;
|
|
}
|
|
|
|
/* Check if all q values are equal */
|
|
for(idx = first_idx; (tmp.s = get_branch(idx, &tmp.len, &q, 0, 0, 0, 0))
|
|
!= 0; idx++) {
|
|
if (q != first_q) {
|
|
goto rest;
|
|
}
|
|
}
|
|
|
|
LM_DBG("t_load_contacts(): nothing to do - all contacts have same q!\n");
|
|
return 1;
|
|
|
|
rest:
|
|
|
|
/* Allocate memory for first contact */
|
|
contacts = (struct contact *)pkg_malloc(sizeof(struct contact));
|
|
if (!contacts) {
|
|
LM_ERR("no memory for contact info\n");
|
|
return -1;
|
|
}
|
|
|
|
if (ruri_is_new) {
|
|
/* Insert Request-URI branch to first contact */
|
|
contacts->uri.s = ruri->s;
|
|
contacts->uri.len = ruri->len;
|
|
contacts->dst_uri = msg->dst_uri;
|
|
contacts->sock = msg->force_send_socket;
|
|
getbflagsval(0, &contacts->flags);
|
|
contacts->path = msg->path_vec;
|
|
} else {
|
|
/* Insert first branch to first contact */
|
|
contacts->uri = uri;
|
|
contacts->dst_uri = dst_uri;
|
|
contacts->sock = sock;
|
|
contacts->flags = flags;
|
|
contacts->path = path;
|
|
}
|
|
contacts->q = first_q;
|
|
contacts->next = (struct contact *)0;
|
|
|
|
/* Insert (remaining) branches to contact list in increasing q order */
|
|
|
|
for(idx = first_idx;
|
|
(uri.s = get_branch(idx,&uri.len,&q,&dst_uri,&path,&flags,&sock))
|
|
!= 0;
|
|
idx++ ) {
|
|
next = (struct contact *)pkg_malloc(sizeof(struct contact));
|
|
if (!next) {
|
|
LM_ERR("no memory for contact info\n");
|
|
free_contact_list(contacts);
|
|
return -1;
|
|
}
|
|
next->uri = uri;
|
|
next->q = q;
|
|
next->dst_uri = dst_uri;
|
|
next->path = path;
|
|
next->flags = flags;
|
|
next->sock = sock;
|
|
next->next = (struct contact *)0;
|
|
prev = (struct contact *)0;
|
|
curr = contacts;
|
|
while (curr && (curr->q < q)) {
|
|
prev = curr;
|
|
curr = curr->next;
|
|
}
|
|
if (!curr) {
|
|
next->next = (struct contact *)0;
|
|
prev->next = next;
|
|
} else {
|
|
next->next = curr;
|
|
if (prev) {
|
|
prev->next = next;
|
|
} else {
|
|
contacts = next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Assign values for q_flags */
|
|
curr = contacts;
|
|
curr->q_flag = 0;
|
|
while (curr->next) {
|
|
if (curr->q < curr->next->q) {
|
|
curr->next->q_flag = Q_FLAG;
|
|
} else {
|
|
curr->next->q_flag = 0;
|
|
}
|
|
curr = curr->next;
|
|
}
|
|
|
|
/* Add contacts to contacts_avp */
|
|
curr = contacts;
|
|
while (curr) {
|
|
if (encode_branch_info(&branch_info, curr) == 0) {
|
|
LM_ERR("encoding of branch info failed\n");
|
|
free_contact_list(contacts);
|
|
if (branch_info.s) pkg_free(branch_info.s);
|
|
return -1;
|
|
}
|
|
val.s = branch_info;
|
|
add_avp(contacts_avp_type|AVP_VAL_STR|(curr->q_flag),
|
|
contacts_avp, val);
|
|
pkg_free(branch_info.s);
|
|
LM_DBG("loaded contact <%.*s> with q_flag <%d>\n",
|
|
STR_FMT(&val.s), curr->q_flag);
|
|
curr = curr->next;
|
|
}
|
|
|
|
/* Clear all branches */
|
|
clear_branches();
|
|
|
|
/* Free contact list */
|
|
free_contact_list(contacts);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Adds to request a new destination set that includes all highest
|
|
* priority class contacts in contacts_avp. Request URI is rewritten with
|
|
* first contact and the remaining contacts (if any) are added as branches.
|
|
* Removes used contacts from contacts_avp. Returns 1, if contacts_avp
|
|
* was not empty and a destination set was successfully added. Returns -2,
|
|
* if contacts_avp was empty and thus there was nothing to do.
|
|
* Returns -1 in case of an error. */
|
|
int t_next_contacts(struct sip_msg* msg, char* key, char* value)
|
|
{
|
|
struct usr_avp *avp, *prev;
|
|
int_str val;
|
|
str uri, dst, path;
|
|
struct socket_info *sock;
|
|
unsigned int flags;
|
|
struct search_state st;
|
|
|
|
/* Check if contacts_avp has been defined */
|
|
if (contacts_avp.n == 0) {
|
|
LM_ERR("feature has been disabled - "
|
|
"to enable define contacts_avp module parameter");
|
|
return -1;
|
|
}
|
|
|
|
/* Load Request-URI and branches */
|
|
|
|
/* Find first contacts_avp value */
|
|
avp = search_first_avp(contacts_avp_type, contacts_avp, &val, &st);
|
|
if (!avp) {
|
|
LM_DBG("no AVPs - we are done!\n");
|
|
return -2;
|
|
}
|
|
|
|
LM_DBG("next contact is <%.*s>\n", STR_FMT(&val.s));
|
|
|
|
if (decode_branch_info(val.s.s, &uri, &dst, &path, &sock, &flags)
|
|
== 0) {
|
|
LM_ERR("decoding of branch info <%.*s> failed\n", STR_FMT(&val.s));
|
|
destroy_avp(avp);
|
|
return -1;
|
|
}
|
|
|
|
/* Rewrite Request-URI */
|
|
rewrite_uri(msg, &uri);
|
|
if (dst.s && dst.len) set_dst_uri(msg, &dst);
|
|
else reset_dst_uri(msg);
|
|
if (path.s && path.len) set_path_vector(msg, &path);
|
|
else reset_path_vector(msg);
|
|
set_force_socket(msg, sock);
|
|
setbflagsval(0, flags);
|
|
|
|
if (avp->flags & Q_FLAG) {
|
|
destroy_avp(avp);
|
|
return 1;
|
|
}
|
|
|
|
/* Append branches until out of branches or Q_FLAG is set */
|
|
prev = avp;
|
|
while ((avp = search_next_avp(&st, &val))) {
|
|
destroy_avp(prev);
|
|
LM_DBG("next contact is <%.*s>\n", STR_FMT(&val.s));
|
|
|
|
if (decode_branch_info(val.s.s, &uri, &dst, &path, &sock, &flags)
|
|
== 0) {
|
|
LM_ERR("decoding of branch info <%.*s> failed\n", STR_FMT(&val.s));
|
|
destroy_avp(avp);
|
|
return -1;
|
|
}
|
|
|
|
if (append_branch(msg, &uri, &dst, &path, 0, flags, sock) != 1) {
|
|
LM_ERR("appending branch failed\n");
|
|
destroy_avp(avp);
|
|
return -1;
|
|
}
|
|
|
|
if (avp->flags & Q_FLAG) {
|
|
destroy_avp(avp);
|
|
return 1;
|
|
}
|
|
prev = avp;
|
|
}
|
|
|
|
destroy_avp(prev);
|
|
|
|
return 1;
|
|
}
|