2
0
Эх сурвалжийг харах

xhttp: new module to handle http requests

- the module can handle http requests sent over TCP/TLS to SIP server
- event_route[xhttp:request] is executed in such case
- if you need to handle HTTP requests without Content-Lenght header, set
tcp_accept_no_cl=1
- it may conflict in HTTP handling with xmlrpc as they use same concept,
  you can set parameter mode=1 for xmrpc module and then call xmlrpc
  functions from event_route[xhttp:request]
- you can use url_match parameter to filter which HTTP requests are
  handled by this module
Daniel-Constantin Mierla 15 жил өмнө
parent
commit
bdba8801cd

+ 14 - 0
modules/xhttp/Makefile

@@ -0,0 +1,14 @@
+# $Id$
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=xhttp.so
+DEFS +=
+LIBS +=
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+include ../../Makefile.modules

+ 114 - 0
modules/xhttp/README

@@ -0,0 +1,114 @@
+xHTTP Module
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+   Copyright © 2010 asipto.com
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Exported Parameters
+
+              3.1. url_match (str)
+
+        4. Exported Functions
+
+              4.1. xhttp_reply(code, reason, ctype, body)
+
+   List of Examples
+
+   1.1. Set url_match parameter
+   1.2. xhttp_reply usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Exported Parameters
+
+        3.1. url_match (str)
+
+   4. Exported Functions
+
+        4.1. xhttp_reply(code, reason, ctype, body)
+
+1. Overview
+
+   This module provides a basic HTTP server functionaly inside SIP server.
+   SIP and HTTP are very similar protocols, practically the SIP parser can
+   handle easy HTTP requests just by adding a fake Via header.
+
+   The xmlrpc module uses the same concept. xHTTP module offers a generic
+   HTTP handling way, by calling event_route[xhttp:request] in your
+   config. You can check the HTTP URI via config variable $hu. Note that
+   $ru will rise errors since URO is not valid for SIP.
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * sl - stateless reply.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * None
+
+3. Exported Parameters
+
+   3.1. url_match (str)
+
+3.1. url_match (str)
+
+   Regular expression to match the HTPP URL. If there is no match, then
+   event route is not executed
+
+   Default value is null (match everything).
+
+   Example 1.1. Set url_match parameter
+...
+modparam("xhttp", "url_match", "^/sip/")
+...
+
+4. Exported Functions
+
+   4.1. xhttp_reply(code, reason, ctype, body)
+
+4.1.  xhttp_reply(code, reason, ctype, body)
+
+   Send back a reply with content-type and body.
+
+   Example 1.2. xhttp_reply usage
+...
+event_route[xhttp:request] {
+    xhttp_reply("200", "OK", "text/html",
+        "<html><body>OK - [$si:$sp]</body></html>");
+}
+...

+ 4 - 0
modules/xhttp/doc/Makefile

@@ -0,0 +1,4 @@
+docs = xhttp.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module

+ 37 - 0
modules/xhttp/doc/xhttp.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+	<title>xHTTP Module</title>
+	<productname class="trade">sip-router.org</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<email>[email protected]</email>
+	    </author>
+	    <editor>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2010</year>
+	    <holder>asipto.com</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+    
+    <xi:include href="xhttp_admin.xml"/>
+    
+    
+</book>

+ 108 - 0
modules/xhttp/doc/xhttp_admin.xml

@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+	
+	<title>&adminguide;</title>
+	
+	<section>
+	<title>Overview</title>
+	<para>
+		This module provides a basic HTTP server functionaly inside SIP
+		server. SIP and HTTP are very similar protocols, practically the
+		SIP parser can handle easy HTTP requests just by adding a fake
+		Via header.
+	</para>
+	<para>
+		The xmlrpc module uses the same concept. xHTTP module offers a
+		generic HTTP handling way, by calling event_route[xhttp:request]
+		in your config. You can check the HTTP URI via config variable
+		$hu. Note that $ru will rise errors since URO is not valid for SIP.
+	</para>
+	</section>
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>sl</emphasis> - stateless reply.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+	<section>
+	<title>Exported Parameters</title>
+	<section>
+		<title><varname>url_match</varname> (str)</title>
+		<para>
+			Regular expression to match the HTPP URL. If there is no match,
+			then event route is not executed
+		</para>
+		<para>
+		<emphasis>
+			Default value is null (match everything).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>url_match</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("xhttp", "url_match", "^/sip/")
+...
+</programlisting>
+		</example>
+	</section>
+	</section>
+
+	<section>
+	<title>Exported Functions</title>
+ 	<section>
+	    <title>
+		<function moreinfo="none">xhttp_reply(code, reason, ctype, body)</function>
+	    </title>
+	    <para>
+		Send back a reply with content-type and body.
+	    </para>
+		<example>
+		<title><function>xhttp_reply</function> usage</title>
+		<programlisting format="linespecific">
+...
+event_route[xhttp:request] {
+    xhttp_reply("200", "OK", "text/html",
+        "&lt;html&gt;&lt;body&gt;OK - [$si:$sp]&lt;/body&gt;&lt;/html&gt;");
+}
+...
+</programlisting>
+	    </example>
+	</section>
+	</section>
+</chapter>
+

+ 468 - 0
modules/xhttp/xhttp_mod.c

@@ -0,0 +1,468 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "../../sr_module.h"
+#include "../../error.h"
+#include "../../mem/mem.h"
+#include "../../ut.h"
+#include "../../dset.h"
+#include "../../dprint.h"
+#include "../../data_lump.h"
+#include "../../data_lump_rpl.h"
+#include "../../receive.h"
+#include "../../msg_translator.h"
+#include "../../modules_k/sl/sl_api.h"
+#include "../../nonsip_hooks.h"
+#include "../../action.h"
+#include "../../script_cb.h"
+#include "../../route.h"
+#include "../../sip_msg_clone.h"
+#include "../../mod_fix.h"
+#include "../../pvar.h"
+
+MODULE_VERSION
+
+static int xhttp_handler(sip_msg_t* msg);
+static int w_xhttp_send_reply(sip_msg_t* msg, char* pcode, char* preason,
+		char *pctype, char* pbody);
+static int mod_init(void);
+
+static int fixup_xhttp_reply(void** param, int param_no);
+
+static int pv_get_huri(struct sip_msg *msg, pv_param_t *param,
+		pv_value_t *res);
+
+static int xhttp_route_no=DEFAULT_RT;
+static char* xhttp_url_match = NULL;
+static regex_t xhttp_url_regexp;
+
+/** SL binds */
+struct sl_binds slb;
+
+
+static cmd_export_t cmds[] = {
+	{"xhttp_reply",    (cmd_function)w_xhttp_send_reply,
+		4, fixup_xhttp_reply,  0, REQUEST_ROUTE},
+	{0, 0, 0, 0, 0}
+};
+
+static pv_export_t mod_pvs[] = {
+	{{"hu", (sizeof("hu")-1)}, /* */
+		PVT_OTHER, pv_get_huri, 0,
+		0, 0, 0, 0},
+
+	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static param_export_t params[] = {
+	{"url_match",       STR_PARAM, &xhttp_url_match},
+	{0, 0, 0}
+};
+
+/** module exports */
+struct module_exports exports= {
+	"xhttp",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,
+	params,
+	0,          /* exported statistics */
+	0  ,        /* exported MI functions */
+	mod_pvs,    /* exported pseudo-variables */
+	0,          /* extra processes */
+	mod_init,   /* module initialization function */
+	0,
+	0,
+	0           /* per-child init function */
+};
+
+/** 
+ * 
+ */
+static int mod_init(void)
+{
+	struct nonsip_hook nsh;
+	int route_no;
+	
+	route_no=route_get(&event_rt, "xhttp:request");
+	if (route_no==-1)
+	{
+		LM_ERR("failed to find event_route[xhttp:request]\n");
+		return -1;
+	}
+	if (event_rt.rlist[route_no]==0)
+	{
+		LM_WARN("event_route[xhttp:request] is empty\n");
+	}
+	xhttp_route_no=route_no;
+	
+	if (load_sl_api(&slb)!=0)
+	{
+		LM_ERR("can't load SL API\n");
+		return -1;
+	}
+
+	/* register non-sip hooks */
+	memset(&nsh, 0, sizeof(nsh));
+	nsh.name = "xhttp";
+	nsh.destroy = 0;
+	nsh.on_nonsip_req = xhttp_handler;
+	if (register_nonsip_msg_hook(&nsh)<0)
+	{
+		LM_ERR("Failed to register non sip msg hooks\n");
+		return -1;
+	}
+
+	if(xhttp_url_match!=NULL)
+	{
+		memset(&xhttp_url_regexp, 0, sizeof(regex_t));
+		if (regcomp(&xhttp_url_regexp, xhttp_url_match, REG_EXTENDED)!=0) {
+			LM_ERR("bad re %s\n", xhttp_url_match);
+			return E_BAD_RE;
+		}
+	}
+	return 0;
+}
+
+/** 
+ * 
+ */
+static int pv_get_huri(struct sip_msg *msg, pv_param_t *param,
+		pv_value_t *res)
+{
+	if(msg==NULL || res==NULL)
+		return -1;
+
+	return pv_get_strval(msg, param, res, &msg->first_line.u.request.uri);
+}
+
+
+/** 
+ * 
+ */
+static char* xhttp_to_sip(sip_msg_t* msg, int* new_msg_len)
+{
+	unsigned int len, via_len;
+	char* via, *new_msg, *p;
+	str ip, port;
+	struct hostport hp;
+	struct dest_info dst;
+	
+	ip.s = ip_addr2a(&msg->rcv.src_ip);
+	ip.len = strlen(ip.s);
+	port.s = int2str(msg->rcv.src_port, &port.len);
+	hp.host = &ip;
+	hp.port = &port;
+	init_dst_from_rcv(&dst, &msg->rcv);
+	via = via_builder(&via_len, &dst, 0, 0, &hp);
+	if (via == 0)
+	{
+		LM_DBG("failed to build via\n");
+		return 0;
+	}
+	len = via_len + msg->len;
+	p = new_msg = pkg_malloc(len + 1);
+	if (new_msg == 0)
+	{
+		LM_DBG("memory allocation failure (%d bytes)\n", len);
+		pkg_free(via);
+		return 0;
+	}
+
+	/* new message:
+	 * <orig first line> 
+	 * Via: <faked via>
+	 * <orig http message w/o the first line>
+	 */
+	memcpy(p, msg->first_line.u.request.method.s, 
+		   msg->first_line.len);
+	p += msg->first_line.len;
+	memcpy(p, via, via_len);
+	p += via_len;
+	memcpy(p,  SIP_MSG_START(msg) + msg->first_line.len, 
+		   msg->len - msg->first_line.len);
+	new_msg[len] = 0;
+	pkg_free(via);
+	*new_msg_len = len;
+	return new_msg;
+}
+
+
+/** 
+ * 
+ */
+static int xhttp_process_request(sip_msg_t* orig_msg, 
+							  char* new_buf, unsigned int new_len)
+{
+	int ret;
+	sip_msg_t tmp_msg, *msg;
+	struct run_act_ctx ra_ctx;
+	
+	ret=0;
+	if (new_buf && new_len)
+	{
+		memset(&tmp_msg, 0, sizeof(sip_msg_t));
+		tmp_msg.buf = new_buf;
+		tmp_msg.len = new_len;
+		tmp_msg.rcv = orig_msg->rcv;
+		tmp_msg.id = orig_msg->id;
+		tmp_msg.set_global_address = orig_msg->set_global_address;
+		tmp_msg.set_global_port = orig_msg->set_global_port;
+		if (parse_msg(new_buf, new_len, &tmp_msg) != 0)
+		{
+			LM_ERR("parse_msg failed\n");
+			goto error;
+		}
+		msg = &tmp_msg;
+	} else {
+		msg = orig_msg;
+	}
+	
+	if ((msg->first_line.type != SIP_REQUEST) || (msg->via1 == 0) ||
+		(msg->via1->error != PARSE_OK))
+	{
+		LM_CRIT("strange message: %.*s\n", msg->len, msg->buf);
+		goto error;
+	}
+	if (exec_pre_script_cb(msg, REQUEST_CB_TYPE) == 0)
+	{
+		goto done;
+	}
+
+	init_run_actions_ctx(&ra_ctx);
+	if (run_actions(&ra_ctx, event_rt.rlist[xhttp_route_no], msg) < 0)
+	{
+		ret=-1;
+		LM_DBG("error while trying script\n");
+		goto done;
+	}
+
+done:
+	exec_post_script_cb(msg, REQUEST_CB_TYPE);
+	if (msg != orig_msg)
+	{
+		free_sip_msg(msg);
+	}
+	return ret;
+
+error:
+	return -1;
+}
+
+
+/** 
+ * 
+ */
+static int xhttp_handler(sip_msg_t* msg)
+{
+	int ret;
+	char* fake_msg;
+	int fake_msg_len;
+	regmatch_t pmatch;
+	char c;
+
+	ret=NONSIP_MSG_DROP;
+
+	if(!IS_HTTP(msg))
+	{
+		/* oly http msg type */
+		return NONSIP_MSG_PASS;
+	}
+
+	if(xhttp_url_match!=NULL)
+	{
+		c = msg->first_line.u.request.uri.s[msg->first_line.u.request.uri.len];
+		msg->first_line.u.request.uri.s[msg->first_line.u.request.uri.len]
+			= '\0';
+		if (regexec(&xhttp_url_regexp, msg->first_line.u.request.uri.s,
+					1, &pmatch, 0)!=0)
+		{
+			LM_DBG("URL not matched\n");
+			msg->first_line.u.request.uri.s[msg->first_line.u.request.uri.len]
+				= c;
+			return NONSIP_MSG_PASS;
+		}
+		msg->first_line.u.request.uri.s[msg->first_line.u.request.uri.len] = c;
+	}
+
+	if (msg->via1 == 0)
+	{
+		fake_msg = xhttp_to_sip(msg, &fake_msg_len);
+		if (fake_msg == 0)
+		{
+			LM_ERR("out of memory\n");
+			ret=NONSIP_MSG_ERROR;
+		} else {
+			DBG("new fake msg created (%d bytes):\n<%.*s>\n",
+					fake_msg_len, fake_msg_len, fake_msg);
+			if (xhttp_process_request(msg, fake_msg, fake_msg_len)<0)
+				ret=NONSIP_MSG_ERROR;
+				pkg_free(fake_msg);
+			}
+			return ret;
+	} else {
+		LM_DBG("http msg unchanged (%d bytes):\n<%.*s>\n",
+				msg->len, msg->len, msg->buf);
+		if (xhttp_process_request(msg, 0, 0)<0)
+			ret=NONSIP_MSG_ERROR;
+		return ret;
+	}
+}
+
+
+/**
+ *
+ */
+static int xhttp_send_reply(sip_msg_t *msg, int code, str *reason,
+		str *ctype, str *body)
+{
+	str tbuf;
+
+	if(ctype->len>0)
+	{
+		/* add content-type */
+		tbuf.len=sizeof("Content-Type: ") - 1 + ctype->len + CRLF_LEN;
+		tbuf.s=pkg_malloc(sizeof(char)*(tbuf.len));
+
+		if (tbuf.len==0)
+		{
+			LM_ERR("out of pkg memory\n");
+			return -1;
+		}
+		memcpy(tbuf.s, "Content-Type: ", sizeof("Content-Type: ") - 1);
+		memcpy(tbuf.s+sizeof("Content-Type: ") - 1, ctype->s, ctype->len);
+		memcpy(tbuf.s+sizeof("Content-Type: ") - 1 + ctype->len,
+				CRLF, CRLF_LEN);
+		if (add_lump_rpl(msg, tbuf.s, tbuf.len, LUMP_RPL_HDR) == 0)
+		{
+			LM_ERR("failed to insert content-type lump\n");
+			pkg_free(tbuf.s);
+			return -1;
+		}
+		pkg_free(tbuf.s);
+	}
+
+	if(body->len>0)
+	{
+		if (add_lump_rpl(msg, body->s, body->len, LUMP_RPL_BODY) < 0)
+		{
+			LM_ERR("Error while adding reply lump\n");
+			return -1;
+		}
+	}
+	if (slb.send_reply(msg, code, reason) < 0)
+	{
+		LM_ERR("Error while sending reply\n");
+		return -1;
+	}
+	return 0;
+}
+
+/**
+ *
+ */
+static int w_xhttp_send_reply(sip_msg_t* msg, char* pcode, char* preason,
+		char *pctype, char* pbody)
+{
+	str body = {0, 0};
+	str reason = {"OK", 2};
+	str ctype = {"text/plain", 10};
+	int code = 200;
+
+	if(pcode==0 || preason==0 || pctype==0 || pbody==0)
+	{
+		LM_ERR("invalid parameters\n");
+		return -1;
+	}
+
+	if(fixup_get_ivalue(msg, (gparam_p)pcode, &code)!=0)
+	{
+		LM_ERR("no reply code value\n");
+		return -1;
+	}
+	if(code<100 || code>700)
+	{
+		LM_ERR("invalid code parameter\n");
+		return -1;
+	}
+
+	if(fixup_get_svalue(msg, (gparam_p)preason, &reason)!=0)
+	{
+		LM_ERR("unable to get reason\n");
+		return -1;
+	}
+	if(reason.s==NULL || reason.len == 0)
+	{
+		LM_ERR("invalid reason parameter\n");
+		return -1;
+	}
+
+	if(fixup_get_svalue(msg, (gparam_p)pctype, &ctype)!=0)
+	{
+		LM_ERR("unable to get content type\n");
+		return -1;
+	}
+	if(ctype.s==NULL)
+	{
+		LM_ERR("invalid content-type parameter\n");
+		return -1;
+	}
+
+	if(fixup_get_svalue(msg, (gparam_p)pbody, &body)!=0)
+	{
+		LM_ERR("unable to get body\n");
+		return -1;
+	}
+	if(body.s==NULL)
+	{
+		LM_ERR("invalid body parameter\n");
+		return -1;
+	}
+
+	if(xhttp_send_reply(msg, code, &reason, &ctype, &body)<0)
+		return -1;
+	return 1;
+}
+
+
+/** 
+ * 
+ */
+static int fixup_xhttp_reply(void** param, int param_no)
+{
+	if (param_no == 1) {
+	    return fixup_igp_null(param, 1);
+	} else if (param_no == 2) {
+	    return fixup_spve_null(param, 1);
+	} else if (param_no == 3) {
+	    return fixup_spve_null(param, 1);
+	} else if (param_no == 4) {
+	    return fixup_spve_null(param, 1);
+	}
+	return 0;
+}
+
+/** @} */