- added plugin_regex

This commit is contained in:
Thomas Ries
2011-06-18 13:53:10 +00:00
parent 6273055626
commit dd94e02127
3 changed files with 406 additions and 3 deletions
+4
View File
@@ -1,5 +1,9 @@
0.8.1
=====
17-Jun-2011: - plugin_prefix: correct handling of outgoing ACKs
(could lead to calls being aborted by one side)
- generic set of "redirect cache" that can be used by
plugins to keep track of Call-IDs when doing "302 Moved"
12-Jun-2011: - one-off in get_ip_by_ifname(), last interface in chain
was not considered in matching.
- Pthread stack size made adjustable (thread_stack_size)
+8 -3
View File
@@ -35,14 +35,16 @@ pkglib_LTLIBRARIES = plugin_demo.la \
plugin_defaulttarget.la \
plugin_fix_bogus_via.la \
plugin_stun.la \
plugin_prefix.la
plugin_prefix.la \
plugin_regex.la
DLOPENPLUGINS = -dlopen plugin_demo.la \
-dlopen plugin_shortdial.la \
-dlopen plugin_logcall.la \
-dlopen plugin_defaulttarget.la \
-dlopen plugin_fix_bogus_via.la \
-dlopen plugin_stun.la \
-dlopen plugin_prefix.la
-dlopen plugin_prefix.la \
-dlopen plugin_regex.la
#
plugin_demo_la_SOURCES = plugin_demo.c
plugin_demo_la_LDFLAGS = -module -avoid-version -shrext '.so'
@@ -64,6 +66,9 @@ plugin_stun_la_LDFLAGS = -module -avoid-version -shrext '.so'
#
plugin_prefix_la_SOURCES = plugin_prefix.c
plugin_prefix_la_LDFLAGS = -module -avoid-version -shrext '.so'
#
plugin_regex_la_SOURCES = plugin_regex.c
plugin_regex_la_LDFLAGS = -module -avoid-version -shrext '.so'
#
@@ -78,7 +83,7 @@ siproxd_SOURCES = siproxd.c proxy.c register.c sock.c utils.c \
sip_utils.c sip_layer.c log.c readconf.c rtpproxy.c \
rtpproxy_relay.c accessctl.c route_processing.c \
security.c auth.c fwapi.c resolve.c \
dejitter.c plugins.c
dejitter.c plugins.c redirect_cache.c
# addrcache.c
+394
View File
@@ -0,0 +1,394 @@
/*
Copyright (C) 2002-2011 Thomas Ries <tries@gmx.net>
This file is part of Siproxd.
Siproxd 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.
Siproxd 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 Siproxd; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* This plugin adds a regular expression rewrite support
* for SIP targets.
*/
/* must be defined before including <plugin.h> */
#define PLUGIN_NAME plugin_regex
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <osipparser2/osip_parser.h>
#include "siproxd.h"
#include "plugins.h"
#include "redirect_cache.h"
#include "log.h"
static char const ident[]="$Id: plugin_regex.c 471 2011-05-28 10:03:49Z hb9xar $";
/* Plug-in identification */
static char name[]="plugin_regex";
static char desc[]="Use regular expressions to rewrite SIP targets";
/* global configuration storage - required for config file location */
extern struct siproxd_config configuration;
/* constants */
#define REDIRECTED_TAG "redirected"
#define REDIRECTED_VAL "regex"
/* plugin configuration storage */
static struct plugin_config {
stringa_t regex_pattern;
stringa_t regex_replace;
} plugin_cfg;
/* Instructions for config parser */
static cfgopts_t plugin_cfg_opts[] = {
{ "plugin_regex_pattern", TYP_STRINGA,&plugin_cfg.regex_pattern, {0, NULL} },
{ "plugin_regex_replace", TYP_STRINGA,&plugin_cfg.regex_replace, {0, NULL} },
{0, 0, 0}
};
/* local storage needed for regular expression handling */
static regex_t *re;
/* Redirect Cache: Queue Head is static */
static redirected_cache_element_t redirected_cache;
/* local prototypes */
static int plugin_regex_init(void);
static int plugin_regex_process(sip_ticket_t *ticket);
static int plugin_regex_redirect(sip_ticket_t *ticket);
regmatch_t * rmatch (char *buf, int size, regex_t *re);
int rreplace (char *buf, int size, regex_t *re, regmatch_t pmatch[], char *rp);
/*
* Plugin API functions code
*/
/* Initialization */
int PLUGIN_INIT(plugin_def_t *plugin_def) {
plugin_def->api_version=SIPROXD_API_VERSION;
plugin_def->name=name;
plugin_def->desc=desc;
plugin_def->exe_mask=PLUGIN_DETERMINE_TARGET;
/* read the config file */
if (read_config(configuration.configfile,
configuration.config_search,
plugin_cfg_opts, name) == STS_FAILURE) {
ERROR("Plugin '%s': could not load config file", name);
return STS_FAILURE;
}
return plugin_regex_init();;
}
/* Processing */
int PLUGIN_PROCESS(int stage, sip_ticket_t *ticket){
int sts;
sts=plugin_regex_process(ticket);
return sts;
}
/* De-Initialization */
int PLUGIN_END(plugin_def_t *plugin_def){
return STS_SUCCESS;
}
/*
* Workload code
*/
int plugin_regex_init(void) {
int i;
int sts, retsts;
int num_entries;
char errbuf[256];
retsts = STS_SUCCESS;
/* check for equal entries of patterns and replacements */
if (plugin_cfg.regex_pattern.used != plugin_cfg.regex_replace.used) {
ERROR("Plugin '%s': number of search patterns (%i) and number of "
"replacement patterns (%i) differ!", name,
plugin_cfg.regex_pattern.used, plugin_cfg.regex_replace.used);
return STS_FAILURE;
}
/* allocate space for regexes and compile them */
num_entries = plugin_cfg.regex_pattern.used;
re = malloc(num_entries*sizeof(re[0]));
for (i=0; i < num_entries; i++) {
sts = regcomp (&re[i], plugin_cfg.regex_pattern.string[i], REG_ICASE);
if (sts != 0) {
regerror(sts, &re[i], errbuf, sizeof(errbuf));
ERROR("Regular expression [%s] failed to compile: %s",
plugin_cfg.regex_pattern.string[i], errbuf);
retsts = STS_FAILURE;
}
}
return retsts;
}
/* returns STS_SIP_SENT if processing is to be terminated,
* otherwise STS_SUCCESS (go on with processing) */
/* code (entry point) */
static int plugin_regex_process(sip_ticket_t *ticket) {
int sts=STS_SUCCESS;
osip_uri_t *req_url;
osip_uri_t *to_url;
osip_generic_param_t *r=NULL;
/* plugin loaded and not configured, return with success */
if (plugin_cfg.regex_pattern.used==0) return STS_SUCCESS;
if (plugin_cfg.regex_replace.used==0) return STS_SUCCESS;
DEBUGC(DBCLASS_PLUGIN,"plugin entered");
req_url=osip_message_get_uri(ticket->sipmsg);
to_url=osip_to_get_url(ticket->sipmsg);
/* only outgoing direction is handled */
sip_find_direction(ticket, NULL);
if (ticket->direction != DIR_OUTGOING)
return STS_SUCCESS;
/* only INVITE and ACK are handled */
if (!MSG_IS_INVITE(ticket->sipmsg) && !MSG_IS_ACK(ticket->sipmsg))
return STS_SUCCESS;
/* expire old cache entries */
expire_redirected_cache(&redirected_cache);
/* REQ URI with username must exist, prefix string must exist */
if (!req_url || !req_url->username)
return STS_SUCCESS; /* ignore */
/* Loop avoidance:
* If this INVITE has already been redirected by a prior 302
* moved response a "REDIRECTED_TAG" parameter should be present in the
* URI.
* Hopefully all UAs (Clients) do honor RFC3261 and copy the
* *full* URI form the contact header into the new request header
* upon a 3xx response.
*/
if (req_url) {
osip_uri_param_get_byname(&(req_url->url_params), REDIRECTED_TAG, &r);
if (r && r->gvalue && strcmp(r->gvalue,REDIRECTED_VAL)== 0) {
DEBUGC(DBCLASS_PLUGIN,"Packet has already been processed (ReqURI)");
return STS_SUCCESS;
}
}
if (to_url) {
osip_uri_param_get_byname(&(to_url->url_params), REDIRECTED_TAG, &r);
if (r && r->gvalue && strcmp(r->gvalue,REDIRECTED_VAL)== 0) {
DEBUGC(DBCLASS_PLUGIN,"Packet has already been processed (ToURI)");
return STS_SUCCESS;
}
}
/*
* The SIP message is to be processed
*/
/* outgoing INVITE request */
if (MSG_IS_INVITE(ticket->sipmsg)) {
DEBUGC(DBCLASS_PLUGIN,"processing INVITE");
sts=plugin_regex_redirect(ticket);
}
/* outgoing ACK request: is result of a local 3xx answer (moved...)
*
* Only consume that particular ACK that belongs to a sent 302 answer,
* nothing else. Otherwise the ACK from the redirected call will get
* consumed as well and causes the call to be aborted (timeout).
* We keep a cache with Call-Ids of such "302 moved" dialogs.
* Only consume such ACKs that are part of such a dialog.
*/
else if (MSG_IS_ACK(ticket->sipmsg)) {
if (is_in_redirected_cache(&redirected_cache, ticket) == STS_TRUE) {
DEBUGC(DBCLASS_PLUGIN,"processing ACK (consume it)");
sts=STS_SIP_SENT; /* eat up the ACK that was directed to myself */
}
}
return sts;
}
/* private plugin code */
static int plugin_regex_redirect(sip_ticket_t *ticket) {
osip_uri_t *to_url=ticket->sipmsg->to->url;
char *to_user=to_url->username;
char *new_to_user=NULL;
int i, sts;
size_t username_len;
osip_contact_t *contact = NULL;
/* character workspaces for regex */
#define WORKSPACE_SIZE 128
static char in[WORKSPACE_SIZE+1], rp[WORKSPACE_SIZE+1];
/* perform search and replace of the regexes, first match hits */
for (i = 0; i < plugin_cfg.regex_pattern.used; i++) {
regmatch_t *pmatch = NULL;
pmatch = rmatch(to_user, WORKSPACE_SIZE, &re[i]);
if (pmatch == NULL) continue; /* no match, next */
/* have a match, do the replacement */
strncpy (in, to_user, WORKSPACE_SIZE);
in[WORKSPACE_SIZE]='\0';
strncpy (rp, plugin_cfg.regex_replace.string[i], WORKSPACE_SIZE);
rp[WORKSPACE_SIZE]='\0';
sts = rreplace(in, WORKSPACE_SIZE, &re[i], pmatch, rp);
if (sts != STS_SUCCESS) {
ERROR("regex replace failed: pattern:[%s] replace:[%s]",
plugin_cfg.regex_pattern.string[i],
plugin_cfg.regex_replace.string[i]);
return STS_FAILURE;
}
break;
}
if (i >= plugin_cfg.regex_pattern.used) {
// no match
return STS_SUCCESS;
}
// in: contains the new string
/* including \0 + leading character(s) */
username_len=strlen(in) + 1;
new_to_user = osip_malloc(username_len); /* *_len excluding \0 */
if (!new_to_user) return STS_SUCCESS;
/* only copy the part that really belongs to the username */
snprintf(new_to_user, username_len, "%s", in );
/* strncpy may not terminate - do it manually to be sure */
new_to_user[username_len-1]='\0';
/* use a "302 Moved temporarily" response back to the client */
/* new target is within the Contact Header */
/* remove all Contact headers in message */
for (i=0; (contact != NULL) || (i == 0); i++) {
osip_message_get_contact(ticket->sipmsg, 0, &contact);
if (contact) {
osip_list_remove(&(ticket->sipmsg->contacts),0);
osip_contact_free(contact);
}
} /* for i */
/* insert one new Contact header containing the new target address */
osip_contact_init(&contact);
osip_uri_clone(to_url, &contact->url);
osip_list_add(&(ticket->sipmsg->contacts),contact,0);
/* USER part is always present, put new_to_user in contact URL */
osip_free(contact->url->username);
contact->url->username=new_to_user;
/*
* Add the 'REDIRECTED_TAG=REDIRECTED_VAL' parameter to URI. Required to figure out
* if this INVITE has already been processed (redirected) and
* does not need further attention by this plugin.
* THIS IS REQUIRED TO AVOID A LOOP
*/
osip_uri_param_add(&(contact->url->url_params), osip_strdup(REDIRECTED_TAG),
osip_strdup(REDIRECTED_VAL));
INFO("redirecting %s -> %s", to_user, new_to_user);
/* sent redirect message back to local client */
add_to_redirected_cache(&redirected_cache, ticket);
sip_gen_response(ticket, 302 /*Moved temporarily*/);
return STS_SIP_SENT;
}
/*
* This regex replacement code has been proudly borrowed from
* http://www.daniweb.com/software-development/c/code/216955#
*
* buf: input string + output result
* rp: replacement string, will be destroyed during processing!
* size: size of buf and rp
* re: regex to process
*
* rmatch() performs the initial regexec match, and if a match is found
* it returns a pointer to the regmatch array which contains the result
* of the match.
* Afterwards rreplace() is to be called, providing this regmatch array.
*
* This eliminates the need to copy the 'rp' string before knowing
* if a match is actually there.
*/
#define NMATCHES 10
regmatch_t * rmatch (char *buf, int size, regex_t *re) {
static regmatch_t pm[NMATCHES]; /* regoff_t is int so size is int */
/* perform the match */
if (regexec (re, buf, NMATCHES, pm, 0)) {
DEBUGC(DBCLASS_PLUGIN,"no match found.");
return NULL;
}
DEBUGC(DBCLASS_PLUGIN,"match found.");
return &pm[0];
}
int rreplace (char *buf, int size, regex_t *re, regmatch_t pmatch[], char *rp) {
char *pos;
int sub, so, n;
/* match(es) found: */
for (pos = rp; *pos; pos++) {
/* back references \1 ... \9: expand them in 'rp' */
if (*pos == '\\' && *(pos + 1) > '0' && *(pos + 1) <= '9') {
so = pmatch[*(pos + 1) - 48].rm_so; /* pmatch[1..9] */
n = pmatch[*(pos + 1) - 48].rm_eo - so;
if (so < 0 || strlen (rp) + n - 1 > size) return STS_FAILURE;
memmove (pos + n, pos + 2, strlen (pos) - 1);
memmove (pos, buf + so, n);
pos = pos + n - 2;
}
}
sub = pmatch[1].rm_so; /* no repeated replace when sub >= 0 */
/* and replace rp in the input buffer */
for (pos = buf; !regexec (re, pos, 1, pmatch, 0); ) {
n = pmatch[0].rm_eo - pmatch[0].rm_so;
pos += pmatch[0].rm_so;
if (strlen (buf) - n + strlen (rp) > size) {
return STS_FAILURE;
}
memmove (pos + strlen (rp), pos + n, strlen (pos) - n + 1);
memmove (pos, rp, strlen (rp));
pos += strlen (rp);
if (sub >= 0) break;
}
return STS_SUCCESS;
}