[Devel] SRV module
Stéphane Alnet
stephane at carrierclass.net
Thu May 10 06:12:01 CEST 2007
Hello,
This is my first attempt at a module for OpenSER. This module is aimed
at providing outbound (from OpenSER) priority/load-balancing for SRV
records. I don't have a compilation environment at hand so I haven't
tried to compile this code yet; I'm mostly looking for feedback on how
well-behaved this code looks to people on the list, and whether there
are SIP-related issues I didn't take care of.
The basic algorithm for srv_branches() is as follows:
- from the message URI, obtain the host (domain) part and use it to do
a SRV query;
- use the records returned by the SRV query to build a list of
branches, ordered by SRV priority (strict) and by somewhat-weighted
randomized priority (if weights) or randomized priority (if all
weights = 0).
In a script, a call to srv_branches() would be followed by
serialized_branches() in order to get the proper result; there is a
snippet script at the top of the srv.c file.
I can envision at least two enhancements to the module: (a) support
hash (as in the dispatcher module) instead of using random() for
target selection; (b) also support NAPTR -- but neither of these are
critical to my application at this time.
Thank you for your feedback. I am not subscribed to the list, so
please Cc: me in your replies.
Stéphane
--
http://carrierclass.net/
http://www.linkedin.com/in/stephalnet
-------------- next part --------------
/**
* $Id$
*
* Copyright (C) 2007 Stephane Alnet (stephane at carrierclass.net)
*
* This file is part of openser, a free SIP server.
*
* openser 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
*
* openser 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 <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include "../../parser/parse_uri.h"
#include "../../dset.h"
#include "../../resolve.h"
#include "../../error.h"
#include "../../mem/mem.h"
/**
* Usage:
*
* route[]
* {
* ...
* setport("0"); # Force SRV resolution
* srv_branches(); # Resolve SRV as branches
* serialize_branches(); # Serialize the branches
* t_relay();
* ...
* }
*
* failure_route[1]
* {
* ...
* next_branches(); # Next serialized branch
* }
*
**/
MODULE_VERSION
/** parameters */
/* int foo=4096; */
/** module functions */
static int mod_init(void);
static int child_init(int);
static int srv_branches(struct sip_msg*, char*, char*);
static int srv_branches_fixup(void** param, int param_no);
void destroy(void);
static cmd_export_t cmds[]={
{"srv_branches", srv_branches, 0, srv_branches_fixup, REQUEST_ROUTE | FAILURE_ROUTE |
ONREPLY_ROUTE | BRANCH_ROUTE},
{0,0,0,0,0}
};
static param_export_t params[]={
/* {"foo", INT_PARAM, &foo}, */
{0,0,0}
};
/** module exports */
struct module_exports exports= {
"srv",
cmds,
params,
0, /* exported statistics */
mod_init, /* module initialization function */
(response_function) 0,
(destroy_function) destroy,
child_init /* per-child init function */
};
/**
* init module function
*/
static int mod_init(void)
{
DBG("SRV: initializing ...\n");
return 0;
}
/**
* Initialize children
*/
static int child_init(int rank)
{
DBG("SRV: init_child [%d] pid [%d]\n", rank, getpid());
return 0;
}
/*
* q = MIN_Q ... MAX_Q (lower q = higher priority)
*
* rdata->priority = 0-65535 [RFC2782] lower value = higher priority (think MX)
* rdata->weight = 0-65535 [RFC2782] algorithm:
* To select a target to be contacted next, arrange all SRV RRs
* (that have not been ordered yet) in any order, except that all
* those with weight 0 are placed at the beginning of the list.
*
* Compute the sum of the weights of those RRs, and with each RR
* associate the running sum in the selected order. Then choose a
* uniform random number between 0 and the sum computed
* (inclusive), and select the RR whose running sum value is the
* first in the selected order which is greater than or equal to
* the random number selected. The target host specified in the
* selected SRV RR is the next one to be contacted by the client.
* Remove this SRV RR from the set of the unordered SRV RRs and
* apply the described algorithm to the unordered SRV RRs to select
* the next target host. Continue the ordering process until there
* are no unordered SRV RRs. This process is repeated for each
* Priority.
*/
/*
* Here we implement a slightly different algorithm.
*/
struct branch_def {
str uri;
unsigned short priority; /* from SRV */
unsigned short weight; /* from SRV */
unsigned order; /* for random selection */
/* order is either a random value, or weight*random value if weight > 0 */
};
/*
*/
int compare_branches( struct branch_def* a, struct branch_def* b )
{
/* Lower priority = preferred */
if( a->priority < b->priority ) return -1;
if( a->priority > b->priority ) return 1;
/* Elements with 0 weight should be last */
if( a->weight == 0 && b->weight > 0 ) return 1;
if( a->weight > 0 && b->weigth == 0 ) return -1;
/* Either both are 0 weight or both are non-zero weight */
if( a->order < b->order ) return -1;
if( a->order > b->order ) return 1;
return 0;
}
/* Maximum number of SRV records we will consider */
#define MAX_BRANCHES 20
/* Should replace this with something similar to the hash functions in dispatcher */
static unsigned long random_msg(struct sip_msg* msg)
{
return random();
}
static int srv_branches(struct sip_msg* msg, char* frm)
{
/* Should this be static or not? */
static branch_def branches[MAX_BRANCHES];
str luri;
struct sip_uri uri;
str name;
struct rdata *head;
struct rdata *rd;
short ncount;
/* Select the RURI to use for SRV resolution */
if (msg->new_uri.s)
luri = msg->new_uri;
else
luri = msg->first_line.u.request.uri;
if (luri.len > MAX_URI_SIZE - 1) {
LOG(L_ERR, "ERROR: srv_branches: too long uri: %.*s\n",
luri.len, luri.s);
return -1;
}
if( parse_uri(luri.s,luri.len,&uri) < 0 ) {
LOG(L_ERR, "ERROR: srv_branches: invalid uri: %.*s\n",
luri.len, luri.s);
return -1;
}
/* Check port == 0 */
if( uri.port_no != 0 ) {
LOG(L_ERR, "ERROR: srv_branches: uri %.*s port is not zero\n",
luri.len, luri.s);
return -1;
}
/* Check proto */
if( uri.proto == PROTO_NONE ) {
LOG(L_ERR, "ERROR: srv_branches: uri %.*s protocol is not known\n",
luri.len, luri.s);
return -1;
}
/* Get the domain name from the RURI */
name = uri.host;
if (name->len >= MAX_DNS_NAME) {
LOG(L_ERR, "ERROR:srv_branches: domain name too long\n");
return 0;
}
if ((name->len+SRV_MAX_PREFIX_LEN+1)>MAX_DNS_NAME) {
LOG(L_WARN, "WARNING:srv_branches: domain name too long (%d),"
" unable to perform SRV lookup\n", name->len);
return 0;
}
/* Build the SRV name */
{
char* tmp;
tmp = pkg_malloc(MAX_DNS_NAME);
if (!tmp){
LOG(L_ERR, "ERROR: srv_branches: memory allocation failure\n");
return E_OUT_OF_MEM;
}
memcpy(tmp, name->s, name->len);
tmp[name->len] = '\0';
switch (*proto) {
case PROTO_UDP:
memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN);
memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len);
tmp[SRV_UDP_PREFIX_LEN + name->len] = '\0';
break;
#ifdef USE_TCP
case PROTO_TCP:
if (tcp_disable) goto err_proto;
memcpy(tmp, SRV_TCP_PREFIX, SRV_TCP_PREFIX_LEN);
memcpy(tmp+SRV_TCP_PREFIX_LEN, name->s, name->len);
tmp[SRV_TCP_PREFIX_LEN + name->len] = '\0';
break;
#endif
#ifdef USE_TLS
case PROTO_TLS:
if (tls_disable) goto err_proto;
memcpy(tmp, SRV_TLS_PREFIX, SRV_TLS_PREFIX_LEN);
memcpy(tmp+SRV_TLS_PREFIX_LEN, name->s, name->len);
tmp[SRV_TLS_PREFIX_LEN + name->len] = '\0';
break;
#endif
default:
LOG(L_ERR, "ERROR:sip_resolvehost: unsupported proto %d\n",
*proto);
return 0;
}
/* Resolve the SRV */
head = get_record(tmp,T_SRV);
pkg_free(tmp);
}
ncount = 0;
/* Build the branch records based on the SRV records */
{
/* Go through the SRV records list */
for( rd=head ; rd ; rd=rd->next )
{
unsigned end, len;
struct srv_data *rdata;
str new_host, new_port;
str new_uri;
char port_s[10];
if(rd->type!=T_SRV )
continue; /* should never happen */
rdata = (struct srv_data*)(rd->rdata);
/* New host */
new_host.s = rdata->name;
new_host.len = rdata->name_len;
/* New port (as string) */
new_port.s = port_s;
new_port.len = snprintf(new_port.s,9,"%d",rdata->port);
/* Build new destination URI */
new_uri.len = 0;
new_uri.s = pkg_malloc(MAX_URI_SIZE);
if (!new_uri.s){
LOG(L_ERR, "ERROR: srv_branches: memory allocation failure\n");
pkg_free(new_port.s);
return E_OUT_OF_MEM;
}
/* Inspired by the code for sethostport in action.c */
#define APPEND(X) \
len=strlen(X); if(new_uri.len+len>MAX_URI_SIZE-1) goto error_uri; \
memcpy(new_uri.s+new_uri.len,X,len); new_uri.len+=len;
#define APPEND_str(X) \
len=X.len; if(new_uri.len+len>MAX_URI_SIZE-1) goto error_uri; \
memcpy(new_uri.s+new_uri.len,X.s,len); new_uri.len+=len;
/* Copy username, password from URI */
APPEND("sip:")
if(uri.user.s)
{
APPEND_str(uri.user)
}
if(uri.passwd.s)
{
APPEND(":")
APPEND_str(uri.passwd)
}
if(uri.user.s)
{
APPEND("@")
}
/* Copy host and port from SRV */
APPEND_str(new_host)
APPEND(":")
APPEND_str(new_port)
/* Copy others from URI */
if(uri.params.s)
{
APPEND(";")
APPEND_str(uri.params)
}
if(uri.headers.s)
{
APPEND("?")
APPEND_str(uri.headers)
}
new_uri.s[new_uri.len] = '\0';
branches[ncount].uri = new_uri;
/* For now use priority plus a non-random order */
branches[ncount].priority = rdata->priority;
branches[ncount].weight = rdata->weight;
branches[ncount].order = random()%65536;
if(rdata->weight != 0)
branches[ncount].order *= rdata->weight;
ncount++;
continue;
error_uri:
pkg_free(new_uri.s);
}
free_rdata_list(head);
}
if( ncount == 0 )
{
LOG(L_ERR, "ERROR: srv_branches: no valid SRV records for %.*s\n",
name.len, name.s);
return -1;
}
/* Sort the values */
qsort(branches,size_of(struct branch_def),MAX_BRANCHES,&compare_branches);
/* Now we append the entries as branches. q are set in increasing order. */
for( q = 0; q < ncount; q++ )
{
append_branch(msg,msg->uri,branches[q].uri,0,q+MIN_Q,0,msg->force_send_socket);
pkg_free(branches[q].uri.s);
}
return 1;
err_proto:
LOG(L_ERR, "ERROR:srv_branches: protocol is disabled\n");
return 0;
}
/**
* destroy function
*/
void destroy(void)
{
DBG("SRV: destroy module ...\n");
}
static int srv_branches_fixup(void** param, int param_no)
{
return 0;
}
More information about the Devel
mailing list