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.
430 lines
10 KiB
430 lines
10 KiB
/**
|
|
* Copyright (C) 2015 Bicom Systems Ltd, (bicomsystems.com)
|
|
*
|
|
* Author: Seudin Kasumovic (seudin.kasumovic@gmail.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#include "mod_erlang.h"
|
|
#include "erl_helpers.h"
|
|
#include "erl_api.h"
|
|
#include "pv_xbuff.h"
|
|
#include "cnode.h"
|
|
|
|
#include <sys/socket.h>
|
|
#include <ei.h>
|
|
|
|
int _impl_api_rpc_call(ei_x_buff* reply, const str *module,const str *function, const ei_x_buff *args);
|
|
int _impl_reg_send(const str *server, const ei_x_buff *msg);
|
|
int _impl_send(const erlang_pid *pid, const ei_x_buff *msg);
|
|
int _impl_reply(const ei_x_buff *msg);
|
|
int xavp2xbuff(ei_x_buff *xbuff, sr_xavp_t *xavp);
|
|
int xbuff2xavp(sr_xavp_t **xavp, ei_x_buff *xbuff);
|
|
|
|
/*!
|
|
* \brief Function exported by module - it will load the other functions
|
|
* \param erl_api Erlang API export binding
|
|
* \return 1
|
|
*/
|
|
int load_erl( erl_api_t *erl_api )
|
|
{
|
|
erl_api->rpc = _impl_api_rpc_call;
|
|
erl_api->reg_send = _impl_reg_send;
|
|
erl_api->send = _impl_send;
|
|
erl_api->reply = _impl_reply;
|
|
erl_api->xavp2xbuff = xavp2xbuff;
|
|
erl_api->xbuff2xavp = xbuff2xavp;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* API implementation
|
|
*/
|
|
int xavp2xbuff(ei_x_buff *xbuff, sr_xavp_t *xavp)
|
|
{
|
|
return xavp_encode(xbuff,xavp,0);
|
|
}
|
|
|
|
|
|
int xbuff2xavp(sr_xavp_t **xavp, ei_x_buff *xbuff)
|
|
{
|
|
int i=0, version=0;
|
|
if (ei_decode_version(xbuff->buff,&i,&version))
|
|
{
|
|
LM_DBG("no version byte encoded in reply\n");
|
|
}
|
|
|
|
return xavp_decode(xbuff,&i,xavp,0);
|
|
}
|
|
|
|
int _impl_api_rpc_call(ei_x_buff *reply, const str *module,const str *function, const ei_x_buff *args)
|
|
{
|
|
struct msghdr msgh;
|
|
struct iovec cnt[8];
|
|
int pid_no = my_pid();
|
|
eapi_t api = API_RPC_CALL;
|
|
int buffsz=0;
|
|
int rc;
|
|
|
|
if (!csockfd) {
|
|
|
|
if (!enode) {
|
|
LM_NOTICE("there is no connected Erlang node\n");
|
|
/* reply up with error */
|
|
ei_x_format(reply, "{error,cnode,~a}", "no_erlang_node");
|
|
return -1;
|
|
}
|
|
|
|
if (rex_call_in_progress) {
|
|
LM_ERR("RPC loop detected\n");
|
|
ei_x_format(reply, "{badrpc,cnode,~a}", "rpc_loop_detected");
|
|
return -1;
|
|
}
|
|
|
|
/* do RPC from event route */
|
|
if (ei_rpc(&enode->ec,enode->sockfd,module->s,function->s,args->buff,args->index,reply) == ERL_ERROR)
|
|
{
|
|
reply->index = 0; /* re-use reply buffer */
|
|
|
|
if (erl_errno)
|
|
{
|
|
ei_x_format(reply, "{error,cnode,~s}", strerror(erl_errno));
|
|
LM_ERR("ei_rpc failed on node=<%s> socket=<%d>: %s\n",enode->conn.nodename,enode->sockfd,strerror(erl_errno));
|
|
}
|
|
else if (errno)
|
|
{
|
|
ei_x_format(reply, "{error,cnode,~s}", strerror(errno));
|
|
LM_ERR("ei_rpc failed on node=<%s> socket=<%d>: %s\n",enode->conn.nodename,enode->sockfd,strerror(errno));
|
|
}
|
|
else
|
|
{
|
|
ei_x_format(reply, "{error,cnode,~s}", "Unknown error.");
|
|
LM_ERR("ei_rpc failed on node=<%s> socket=<%d>, Unknown error.\n",enode->ec.thisalivename,enode->sockfd);
|
|
}
|
|
return -1;
|
|
}
|
|
/* reset response */
|
|
enode->response.index = 0;
|
|
return 0;
|
|
}
|
|
|
|
memset(&msgh, 0, sizeof(msgh));
|
|
memset(&cnt, 0, sizeof(cnt));
|
|
|
|
/* Kamailio PID */
|
|
cnt[0].iov_base = (void*)&pid_no;
|
|
cnt[0].iov_len = sizeof(pid_no);
|
|
|
|
/* method */
|
|
cnt[1].iov_base = (void*)&api;
|
|
cnt[1].iov_len = sizeof(api);
|
|
|
|
/* put size of following data */
|
|
cnt[2].iov_base = (void*)&module->len;
|
|
cnt[2].iov_len = sizeof(int);
|
|
|
|
cnt[3].iov_base = (void*)&function->len;
|
|
cnt[3].iov_len = sizeof(int);
|
|
|
|
buffsz = args->index; /* occupied size */
|
|
cnt[4].iov_base = (void*) &buffsz;
|
|
cnt[4].iov_len = sizeof(buffsz);
|
|
|
|
/* module name */
|
|
cnt[5].iov_base = (void*)module->s;
|
|
cnt[5].iov_len = module->len;
|
|
|
|
/* function name */
|
|
cnt[6].iov_base = (void*)function->s;
|
|
cnt[6].iov_len = function->len;
|
|
|
|
/* Erlang arguments content */
|
|
cnt[7].iov_base = (void*)args->buff;
|
|
cnt[7].iov_len = buffsz; /* occupied size */
|
|
|
|
msgh.msg_iov = cnt;
|
|
msgh.msg_iovlen = 8;
|
|
|
|
while ((rc = sendmsg(csockfd, &msgh, 0)) == -1 && errno == EAGAIN)
|
|
;
|
|
|
|
if (rc == -1) {
|
|
LM_ERR("sendmsg failed: %s\n",strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
/*receive into reply buffer */
|
|
cnt[1].iov_base = &buffsz;
|
|
cnt[1].iov_len = sizeof(buffsz);
|
|
|
|
/* peek reply size safe */
|
|
msgh.msg_iovlen = 2;
|
|
while ((rc = recvmsg(csockfd, &msgh, MSG_PEEK)) == -1 && errno == EAGAIN)
|
|
;
|
|
|
|
if (rc == -1) {
|
|
LM_ERR("recvmsg failed: %s\n",strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (reply->buffsz < buffsz) {
|
|
ei_x_free(reply);
|
|
reply->buffsz = buffsz + 1;
|
|
reply->buff = (char*)malloc(reply->buffsz);
|
|
}
|
|
|
|
cnt[2].iov_base = (void*)reply->buff;
|
|
cnt[2].iov_len = buffsz;
|
|
|
|
msgh.msg_iovlen = 3;
|
|
while ((rc = recvmsg(csockfd, &msgh, MSG_WAITALL)) == -1 && errno == EAGAIN)
|
|
;
|
|
|
|
if (rc == -1) {
|
|
LM_ERR("recvmsg failed: %s\n",strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if(pid_no != my_pid()) {
|
|
/* should never happened */
|
|
LM_CRIT("BUG: got other process reply (pid_no=%d)\n",pid_no);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int _impl_reg_send(const str *server, const ei_x_buff *msg)
|
|
{
|
|
struct msghdr msgh;
|
|
struct iovec cnt[6];
|
|
int pid_no = my_pid();
|
|
eapi_t api = API_REG_SEND;
|
|
int buffsz;
|
|
int rc;
|
|
int i=0,version;
|
|
|
|
memset(&msgh, 0, sizeof(msgh));
|
|
memset(&cnt, 0, sizeof(cnt));
|
|
|
|
if (ei_decode_version(msg->buff,&i,&version)) {
|
|
LM_ERR("msg must be encoded with version\n");
|
|
return -1;
|
|
}
|
|
|
|
if (!csockfd) {
|
|
|
|
if (!enode) {
|
|
LM_NOTICE("there is no connected Erlang node\n");
|
|
return -1;
|
|
}
|
|
|
|
if (rex_call_in_progress) {
|
|
LM_ERR("RPC in progress detected\n");
|
|
return -1;
|
|
}
|
|
|
|
/* do ERL_REG_SEND in event route */
|
|
if ((rc = ei_reg_send(&enode->ec,enode->sockfd,server->s,msg->buff,msg->buffsz)) == ERL_ERROR)
|
|
{
|
|
if (erl_errno)
|
|
{
|
|
LM_ERR("ei_reg_send failed on node=<%s> socket=<%d>: %s\n",enode->conn.nodename,enode->sockfd,strerror(erl_errno));
|
|
}
|
|
else if (errno)
|
|
{
|
|
LM_ERR("ei_reg_send failed on node=<%s> socket=<%d>: %s\n",enode->conn.nodename,enode->sockfd,strerror(errno));
|
|
}
|
|
else
|
|
{
|
|
LM_ERR("ei_reg_send failed on node=<%s> socket=<%d>, Unknown error.\n",enode->ec.thisalivename,enode->sockfd);
|
|
}
|
|
}
|
|
/* reset response */
|
|
enode->response.index = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* Kamailio PID */
|
|
cnt[0].iov_base = (void*)&pid_no;
|
|
cnt[0].iov_len = sizeof(pid_no);
|
|
|
|
/* method */
|
|
cnt[1].iov_base = (void*)&api;
|
|
cnt[1].iov_len = sizeof(api);
|
|
|
|
/* put size of following data */
|
|
cnt[2].iov_base = (void*)&server->len;
|
|
cnt[2].iov_len = sizeof(int);
|
|
|
|
buffsz = msg->index; /* occupied size */
|
|
cnt[3].iov_base = (void*)&buffsz;
|
|
cnt[3].iov_len = sizeof(buffsz);
|
|
|
|
/* module name */
|
|
cnt[4].iov_base = (void*)server->s;
|
|
cnt[4].iov_len = server->len;
|
|
|
|
/* Erlang arguments content */
|
|
cnt[5].iov_base = (void*)msg->buff;
|
|
cnt[5].iov_len = buffsz; /* occupied size */
|
|
|
|
msgh.msg_iov = cnt;
|
|
msgh.msg_iovlen = 6;
|
|
|
|
while ((rc = sendmsg(csockfd, &msgh, 0)) == -1 && errno == EAGAIN)
|
|
;
|
|
|
|
if (rc == -1) {
|
|
LM_ERR("sendmsg failed: %s\n",strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
/* no reply */
|
|
|
|
return 0;
|
|
}
|
|
|
|
int _impl_reply(const ei_x_buff *msg)
|
|
{
|
|
int i=0,version;
|
|
|
|
if (ei_decode_version(msg->buff,&i,&version)) {
|
|
LM_ERR("msg must be encoded with version\n");
|
|
return -1;
|
|
}
|
|
|
|
/* must be in call back / event route */
|
|
|
|
if (csockfd) {
|
|
LM_ERR("not in callback\n");
|
|
return -1;
|
|
} else if (!enode) {
|
|
LM_ERR("not connected\n");
|
|
return -1;
|
|
}
|
|
|
|
if (rex_call_in_progress) {
|
|
LM_ERR("RPC in progress detected\n");
|
|
return -1;
|
|
}
|
|
|
|
/* copy into reply */
|
|
if (enode->response.buffsz < msg->buffsz) {
|
|
/* realocate */
|
|
enode->response.buff=realloc(enode->response.buff,msg->buffsz);
|
|
if (!enode->response.buff) {
|
|
LM_ERR("realloc failed: not enough memory\n");
|
|
return -1;
|
|
}
|
|
enode->response.buffsz = msg->buffsz;
|
|
}
|
|
|
|
memcpy((void*)enode->response.buff,(void*)msg->buff,msg->buffsz);
|
|
enode->response.index = msg->index;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int _impl_send(const erlang_pid *pid, const ei_x_buff *msg)
|
|
{
|
|
struct msghdr msgh;
|
|
struct iovec cnt[6];
|
|
int pid_no = my_pid();
|
|
eapi_t api = API_SEND;
|
|
int buffsz;
|
|
int rc;
|
|
int i=0,version;
|
|
|
|
if (ei_decode_version(msg->buff,&i,&version)) {
|
|
LM_ERR("msg must be encoded with version\n");
|
|
return -1;
|
|
}
|
|
|
|
if (rex_call_in_progress) {
|
|
LM_ERR("RPC in progress detected\n");
|
|
return -1;
|
|
}
|
|
|
|
if (enode) {
|
|
|
|
/* copy into reply */
|
|
if (enode->response.buffsz < msg->buffsz) {
|
|
/* reallocate */
|
|
enode->response.buff=realloc(enode->response.buff,msg->buffsz);
|
|
if (!enode->response.buff) {
|
|
LM_ERR("realloc failed: not enough memory\n");
|
|
return -1;
|
|
}
|
|
enode->response.buffsz = msg->buffsz;
|
|
}
|
|
|
|
memcpy((void*)enode->response.buff,(void*)msg->buff,msg->buffsz);
|
|
enode->response.index = msg->index;
|
|
|
|
/* address process */
|
|
cnode_reply_to_pid = (erlang_pid *)pid;
|
|
return 0;
|
|
} else if (csockfd) {
|
|
|
|
/* send via cnode */
|
|
memset(&msgh, 0, sizeof(msgh));
|
|
memset(&cnt, 0, sizeof(cnt));
|
|
|
|
/* Kamailio PID */
|
|
cnt[0].iov_base = (void*)&pid_no;
|
|
cnt[0].iov_len = sizeof(pid_no);
|
|
|
|
/* method */
|
|
cnt[1].iov_base = (void*)&api;
|
|
cnt[1].iov_len = sizeof(api);
|
|
|
|
/* put size of following data */
|
|
buffsz = msg->index; /* occupied size */
|
|
cnt[2].iov_base = (void*)&buffsz;
|
|
cnt[2].iov_len = sizeof(buffsz);
|
|
|
|
/* module name */
|
|
cnt[3].iov_base = (void*)pid;
|
|
cnt[3].iov_len = sizeof(erlang_pid);
|
|
|
|
/* Erlang arguments content */
|
|
cnt[4].iov_base = (void*)msg->buff;
|
|
cnt[4].iov_len = buffsz; /* occupied size */
|
|
|
|
msgh.msg_iov = cnt;
|
|
msgh.msg_iovlen = 5;
|
|
|
|
while ((rc = sendmsg(csockfd, &msgh, 0)) == -1 && errno == EAGAIN)
|
|
;
|
|
|
|
if (rc == -1) {
|
|
LM_ERR("sendmsg failed: %s\n",strerror(errno));
|
|
return -1;
|
|
}
|
|
} else {
|
|
LM_ERR("not connected\n");
|
|
return -1;
|
|
}
|
|
|
|
/* no reply */
|
|
return 0;
|
|
}
|