|
@@ -0,0 +1,323 @@
|
|
|
|
+/*
|
|
|
|
+ * Prefix Route Module
|
|
|
|
+ *
|
|
|
|
+ * Copyright (C) 2007 Alfred E. Heggestad
|
|
|
|
+ * Copyright (C) 2008 Telio Telecom AS
|
|
|
|
+ *
|
|
|
|
+ * 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:
|
|
|
|
+ * [email protected]
|
|
|
|
+ *
|
|
|
|
+ * 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 <stdlib.h>
|
|
|
|
+#include "../../db/db.h"
|
|
|
|
+#include "../../rpc.h"
|
|
|
|
+#include "../../sr_module.h"
|
|
|
|
+#include "../../mem/mem.h"
|
|
|
|
+#include "../../data_lump_rpl.h"
|
|
|
|
+#include "../../parser/parse_uri.h"
|
|
|
|
+#include "../../parser/msg_parser.h"
|
|
|
|
+#include "../../locking.h"
|
|
|
|
+#include "../../action.h"
|
|
|
|
+#include "../../route.h"
|
|
|
|
+#include "tree.h"
|
|
|
|
+#include "pr.h"
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+MODULE_VERSION
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* Modules parameters */
|
|
|
|
+static char *db_url = DEFAULT_DB_URL;
|
|
|
|
+static char *db_table = "prefix_route";
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static int add_route(struct tree_item *root, const char *prefix,
|
|
|
|
+ const char *route)
|
|
|
|
+{
|
|
|
|
+ int ix, err;
|
|
|
|
+
|
|
|
|
+ /* We cache the route index here so we can avoid route_lookup()
|
|
|
|
+ * in the prefix_route() routing function.
|
|
|
|
+ */
|
|
|
|
+ ix = route_lookup(&main_rt, (char *)route);
|
|
|
|
+ if (ix < 0) {
|
|
|
|
+ LOG(L_CRIT, "prefix_route: db_load(): "
|
|
|
|
+ "route name '%s' is not defined\n", route);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (ix >= main_rt.entries) {
|
|
|
|
+ LOG(L_CRIT, "prefix_route: route %d > n_entries (%d)\n",
|
|
|
|
+ ix, main_rt.entries);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = tree_item_add(root, prefix, route, ix);
|
|
|
|
+ if (0 != err) {
|
|
|
|
+ LOG(L_CRIT, "prefix_route: db_load(): "
|
|
|
|
+ "tree_item_add() failed (%d)\n", err);
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Load prefix rules from database and build tree in memory
|
|
|
|
+ */
|
|
|
|
+int pr_db_load(void)
|
|
|
|
+{
|
|
|
|
+ db_ctx_t *ctx;
|
|
|
|
+ db_cmd_t *cmd = NULL;
|
|
|
|
+ db_res_t* res = NULL;
|
|
|
|
+ db_rec_t* rec;
|
|
|
|
+ struct tree_item *root;
|
|
|
|
+ int err = -1;
|
|
|
|
+ int count = 0;
|
|
|
|
+ db_fld_t match[] = {
|
|
|
|
+ { .name = "prefix", .type = DB_CSTR },
|
|
|
|
+ { .name = "route", .type = DB_CSTR },
|
|
|
|
+ { .name = "comment", .type = DB_CSTR },
|
|
|
|
+ { .name = NULL, .type = DB_NONE }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ ctx = db_ctx("prefix_route");
|
|
|
|
+ if (!ctx) {
|
|
|
|
+ LOG(L_ERR, "prefix_route: db_load(): db_ctx() failed\n");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ if (db_add_db(ctx, db_url) < 0) {
|
|
|
|
+ LOG(L_ERR, "prefix_route: db_load(): could not add db\n");
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ if (db_connect(ctx) < 0) {
|
|
|
|
+ LOG(L_ERR, "prefix_route: db_load(): could not connect\n");
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cmd = db_cmd(DB_GET, ctx, db_table, match, NULL, NULL);
|
|
|
|
+ if (!cmd) {
|
|
|
|
+ LOG(L_ERR, "prefix_route: db_load(): db_cmd() failed\n");
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (db_exec(&res, cmd) < 0) {
|
|
|
|
+ LOG(L_ERR, "prefix_route: db_load(): db_exec() failed\n");
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ root = tree_item_alloc();
|
|
|
|
+ if (NULL == root) {
|
|
|
|
+ LOG(L_ERR, "prefix_route: db_load() tree alloc failed\n");
|
|
|
|
+ err = -1;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Assume Success */
|
|
|
|
+ err = 0;
|
|
|
|
+
|
|
|
|
+ /* Read from DB and build tree */
|
|
|
|
+ for (rec = db_first(res); rec != NULL && !err; rec = db_next(res)) {
|
|
|
|
+ const char *prefix, *route, *comment;
|
|
|
|
+
|
|
|
|
+ ++count;
|
|
|
|
+
|
|
|
|
+ if (rec->fld[0].flags & DB_NULL) {
|
|
|
|
+ LOG(L_CRIT, "prefix_route: ERROR: bad 'prefix' "
|
|
|
|
+ "record in table %s, skipping...\n", db_table);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ if (rec->fld[1].flags & DB_NULL) {
|
|
|
|
+ LOG(L_CRIT, "prefix_route: ERROR: bad 'route' "
|
|
|
|
+ "record in table %s, skipping...\n", db_table);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ prefix = rec->fld[0].v.cstr;
|
|
|
|
+ route = rec->fld[1].v.cstr;
|
|
|
|
+ comment = rec->fld[2].v.cstr;
|
|
|
|
+
|
|
|
|
+ LOG(L_INFO, " %d: prefix=%-10s route=%-15s comment=%s\n",
|
|
|
|
+ count, prefix, route, comment);
|
|
|
|
+
|
|
|
|
+ err = add_route(root, prefix, route);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ LOG(L_NOTICE, "prefix_route: Total prefix routes loaded: %d\n", count);
|
|
|
|
+
|
|
|
|
+ /* Error */
|
|
|
|
+ if (0 != err) {
|
|
|
|
+ LOG(L_ERR, "prefix_route: db_load(): error, flushing tree\n");
|
|
|
|
+ tree_item_free(root);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Swap trees */
|
|
|
|
+ err = tree_swap(root);
|
|
|
|
+ if (0 != err)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ out:
|
|
|
|
+ /* Free database results */
|
|
|
|
+ if (res)
|
|
|
|
+ db_res_free(res);
|
|
|
|
+ if (cmd)
|
|
|
|
+ db_cmd_free(cmd);
|
|
|
|
+
|
|
|
|
+ /* Close database connection */
|
|
|
|
+ if (ctx)
|
|
|
|
+ db_ctx_free(ctx);
|
|
|
|
+
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Initialize module
|
|
|
|
+ */
|
|
|
|
+static int mod_init(void)
|
|
|
|
+{
|
|
|
|
+ /* Initialise tree */
|
|
|
|
+ if (0 != tree_init()) {
|
|
|
|
+ LOG(L_CRIT, "prefix_route: tree_init() failed\n\n");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Populate database */
|
|
|
|
+ if (0 != pr_db_load()) {
|
|
|
|
+ LOG(L_CRIT, "prefix_route: db_load() failed\n\n");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Destroy module and free all resources
|
|
|
|
+ */
|
|
|
|
+static void mod_destroy(void)
|
|
|
|
+{
|
|
|
|
+ tree_close();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Extract username from the Request URI
|
|
|
|
+ * First try to look at the original Request URI and if there
|
|
|
|
+ * is no username use the new Request URI
|
|
|
|
+ */
|
|
|
|
+static int get_username(struct sip_msg* msg, str *user)
|
|
|
|
+{
|
|
|
|
+ if (!msg || !user)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ if (parse_sip_msg_uri(msg) < 0){
|
|
|
|
+ LOG(L_ERR, "get_username(): bad uri\n");
|
|
|
|
+ return -1; /* error, bad uri */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (msg->parsed_uri.user.s == 0){
|
|
|
|
+ /* no user in uri */
|
|
|
|
+ LOG(L_ERR, "get_username(): no user in uri\n");
|
|
|
|
+ return -2;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ *user = msg->parsed_uri.user;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * SER Command "prefix_route"
|
|
|
|
+ */
|
|
|
|
+static int prefix_route(struct sip_msg *msg, char *p1, char *p2)
|
|
|
|
+{
|
|
|
|
+ struct run_act_ctx ra_ctx;
|
|
|
|
+ str user;
|
|
|
|
+ int err;
|
|
|
|
+ int route;
|
|
|
|
+
|
|
|
|
+ /* Unused */
|
|
|
|
+ (void)p1;
|
|
|
|
+ (void)p2;
|
|
|
|
+
|
|
|
|
+ /* Get request URI */
|
|
|
|
+ err = get_username(msg, &user);
|
|
|
|
+ if (0 != err) {
|
|
|
|
+ LOG(L_ERR, "prefix_route: could not get username in"
|
|
|
|
+ " Request URI (%d)\n", err);
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ route = tree_route_get(&user);
|
|
|
|
+ if (route <= 0)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ /* If match send to route[x] */
|
|
|
|
+ init_run_actions_ctx(&ra_ctx);
|
|
|
|
+
|
|
|
|
+ err = run_actions(&ra_ctx, main_rt.rlist[route], msg);
|
|
|
|
+ if (err < 0) {
|
|
|
|
+ LOG(L_ERR, "prefix_route: run_actions failed (%d)\n", err);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Success */
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Exported functions
|
|
|
|
+ */
|
|
|
|
+static cmd_export_t cmds[] = {
|
|
|
|
+ {"prefix_route", prefix_route, 0, 0, REQUEST_ROUTE},
|
|
|
|
+ {0, 0, 0, 0, 0 }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Exported parameters
|
|
|
|
+ */
|
|
|
|
+static param_export_t params[] = {
|
|
|
|
+ {"db_url", STR_PARAM, &db_url },
|
|
|
|
+ {"db_table", STR_PARAM, &db_table},
|
|
|
|
+ {0, 0, 0 }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Module description
|
|
|
|
+ */
|
|
|
|
+struct module_exports exports = {
|
|
|
|
+ "prefix_route", /* Module name */
|
|
|
|
+ cmds, /* Exported functions */
|
|
|
|
+ pr_rpc, /* RPC methods */
|
|
|
|
+ params, /* Exported parameters */
|
|
|
|
+ mod_init, /* Initialization function */
|
|
|
|
+ 0, /* Response function */
|
|
|
|
+ mod_destroy, /* Destroy function */
|
|
|
|
+ 0, /* OnCancel function */
|
|
|
|
+ 0 /* Child init function */
|
|
|
|
+};
|