|
@@ -23,12 +23,19 @@
|
|
|
#include <stdlib.h>
|
|
|
#include <stdio.h>
|
|
|
#include <stdarg.h>
|
|
|
+#include <sys/stat.h>
|
|
|
+#include <sys/types.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <errno.h>
|
|
|
+#include <fcntl.h>
|
|
|
|
|
|
#include "../../ver.h"
|
|
|
#include "../../trim.h"
|
|
|
+#include "../../pt.h"
|
|
|
#include "../../sr_module.h"
|
|
|
#include "../../mod_fix.h"
|
|
|
#include "../../nonsip_hooks.h"
|
|
|
+#include "../../cfg/cfg_struct.h"
|
|
|
#include "../../modules/xhttp/api.h"
|
|
|
|
|
|
#include "jsonrpc-s_mod.h"
|
|
@@ -53,8 +60,19 @@ MODULE_VERSION
|
|
|
#define jsonrpc_malloc pkg_malloc
|
|
|
#define jsonrpc_free pkg_free
|
|
|
|
|
|
-str JSONRPC_REASON_OK = str_init("OK");
|
|
|
-str JSONRPC_CONTENT_TYPE_HTML = str_init("application/json");
|
|
|
+static str JSONRPC_REASON_OK = str_init("OK");
|
|
|
+static str JSONRPC_CONTENT_TYPE_HTML = str_init("application/json");
|
|
|
+
|
|
|
+/* FIFO server vars */
|
|
|
+static char *jsonrpc_fifo = NULL; /*!< FIFO file name */
|
|
|
+static char *jsonrpc_fifo_reply_dir = "/tmp/"; /*!< dir where reply fifos are allowed */
|
|
|
+static int jsonrpc_fifo_uid = -1; /*!< Fifo default UID */
|
|
|
+static char *jsonrpc_fifo_uid_s = 0; /*!< Fifo default User ID name */
|
|
|
+static int jsonrpc_fifo_gid = -1; /*!< Fifo default Group ID */
|
|
|
+static char *jsonrpc_fifo_gid_s = 0; /*!< Fifo default Group ID name */
|
|
|
+static int jsonrpc_fifo_mode = S_IRUSR| S_IWUSR| S_IRGRP| S_IWGRP; /* Default file mode rw-rw---- */
|
|
|
+
|
|
|
+static int jsonrpc_transport = 0; /*!< 0 - all available; 1 - http; 2 - fifo */
|
|
|
|
|
|
static int jsonrpc_pretty_format = 0;
|
|
|
|
|
@@ -62,9 +80,12 @@ static int jsonrpc_register_rpc(void);
|
|
|
|
|
|
static int mod_init(void);
|
|
|
static int child_init(int rank);
|
|
|
+static void mod_destroy(void);
|
|
|
static int jsonrpc_dispatch(sip_msg_t* msg, char* s1, char* s2);
|
|
|
static int jsonrpc_exec(sip_msg_t* msg, char* cmd, char* s2);
|
|
|
|
|
|
+static int jsonrpc_init_fifo_file(void);
|
|
|
+static void jsonrpc_fifo_process(int rank);
|
|
|
|
|
|
/** The context of the jsonrpc request being processed.
|
|
|
*
|
|
@@ -95,7 +116,15 @@ static cmd_export_t cmds[] = {
|
|
|
};
|
|
|
|
|
|
static param_export_t params[] = {
|
|
|
- {"pretty_format", PARAM_INT, &jsonrpc_pretty_format},
|
|
|
+ {"pretty_format", PARAM_INT, &jsonrpc_pretty_format},
|
|
|
+ {"fifo_name", PARAM_STRING, &jsonrpc_fifo},
|
|
|
+ {"fifo_mode", PARAM_INT, &jsonrpc_fifo_mode},
|
|
|
+ {"fifo_group", PARAM_STRING, &jsonrpc_fifo_gid_s},
|
|
|
+ {"fifo_group", PARAM_INT, &jsonrpc_fifo_gid},
|
|
|
+ {"fifo_user", PARAM_STRING, &jsonrpc_fifo_uid_s},
|
|
|
+ {"fifo_user", PARAM_INT, &jsonrpc_fifo_uid},
|
|
|
+ {"fifo_reply_dir", PARAM_STRING, &jsonrpc_fifo_reply_dir},
|
|
|
+ {"transport", PARAM_INT, &jsonrpc_transport},
|
|
|
{0, 0, 0}
|
|
|
};
|
|
|
|
|
@@ -120,7 +149,7 @@ struct module_exports exports= {
|
|
|
0, /* extra processes */
|
|
|
mod_init, /* module initialization function */
|
|
|
0,
|
|
|
- 0,
|
|
|
+ mod_destroy,/* module destroy function */
|
|
|
child_init /* per-child init function */
|
|
|
};
|
|
|
|
|
@@ -317,6 +346,7 @@ static int jsonrpc_send(jsonrpc_ctx_t* ctx)
|
|
|
} else {
|
|
|
jsonrpc_set_plain_reply(ctx->http_code, &ctx->http_text, &rbuf,
|
|
|
ctx->jrpl->free_fn);
|
|
|
+ rbuf.s=NULL;
|
|
|
}
|
|
|
} else {
|
|
|
if(ctx->msg) {
|
|
@@ -864,9 +894,25 @@ static void jsonrpc_clean_context(jsonrpc_ctx_t* ctx)
|
|
|
static int mod_init(void)
|
|
|
{
|
|
|
/* bind the XHTTP API */
|
|
|
- if (xhttp_load_api(&xhttp_api) < 0) {
|
|
|
- LM_ERR("cannot bind to XHTTP API\n");
|
|
|
- return -1;
|
|
|
+ if(jsonrpc_transport==0 || jsonrpc_transport==1) {
|
|
|
+ if (xhttp_load_api(&xhttp_api) < 0) {
|
|
|
+ if(jsonrpc_transport==1) {
|
|
|
+ LM_ERR("cannot bind to XHTTP API\n");
|
|
|
+ return -1;
|
|
|
+ } else {
|
|
|
+ memset(&xhttp_api, 0, sizeof(xhttp_api_t));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(jsonrpc_transport==0 || jsonrpc_transport==2) {
|
|
|
+ if(jsonrpc_init_fifo_file()<0) {
|
|
|
+ if(jsonrpc_transport==2) {
|
|
|
+ LM_ERR("cannot initialize fifo transport\n");
|
|
|
+ return -1;
|
|
|
+ } else {
|
|
|
+ jsonrpc_fifo = NULL;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
memset(&func_param, 0, sizeof(func_param));
|
|
@@ -892,12 +938,61 @@ static int mod_init(void)
|
|
|
|
|
|
static int child_init(int rank)
|
|
|
{
|
|
|
+ int pid;
|
|
|
+
|
|
|
+ if (rank==PROC_MAIN) {
|
|
|
+ if(jsonrpc_fifo != NULL) {
|
|
|
+ pid=fork_process(PROC_NOCHLDINIT, "JSONRPC-S FIFO", 1);
|
|
|
+ if (pid<0)
|
|
|
+ return -1; /* error */
|
|
|
+ if(pid==0) {
|
|
|
+ /* child */
|
|
|
+
|
|
|
+ /* initialize the config framework */
|
|
|
+ if (cfg_child_init())
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ jsonrpc_fifo_process(1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if(rank==PROC_MAIN || rank==PROC_TCP_MAIN)
|
|
|
+ {
|
|
|
return 0; /* do nothing for the main process */
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void mod_destroy(void)
|
|
|
+{
|
|
|
+ int n;
|
|
|
+ struct stat filestat;
|
|
|
+
|
|
|
+ if(jsonrpc_fifo==NULL)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* destroying the fifo file */
|
|
|
+ n=stat(jsonrpc_fifo, &filestat);
|
|
|
+ if (n==0){
|
|
|
+ /* FIFO exist, delete it (safer) if not config check */
|
|
|
+ if(config_check==0) {
|
|
|
+ if (unlink(jsonrpc_fifo)<0){
|
|
|
+ LM_ERR("cannot delete the fifo (%s): %s\n",
|
|
|
+ jsonrpc_fifo, strerror(errno));
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (n<0 && errno!=ENOENT) {
|
|
|
+ LM_ERR("FIFO stat failed: %s\n", strerror(errno));
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+error:
|
|
|
+ return;
|
|
|
+}
|
|
|
|
|
|
static int jsonrpc_dispatch(sip_msg_t* msg, char* s1, char* s2)
|
|
|
{
|
|
@@ -979,7 +1074,7 @@ send_reply:
|
|
|
}
|
|
|
|
|
|
|
|
|
-static int jsonrpc_exec(sip_msg_t* msg, char* cmd, char* s2)
|
|
|
+static int jsonrpc_exec_ex(str *cmd, str *rpath)
|
|
|
{
|
|
|
rpc_export_t* rpce;
|
|
|
jsonrpc_ctx_t* ctx;
|
|
@@ -988,10 +1083,7 @@ static int jsonrpc_exec(sip_msg_t* msg, char* cmd, char* s2)
|
|
|
str val;
|
|
|
str scmd;
|
|
|
|
|
|
- if(fixup_get_svalue(msg, (gparam_t*)cmd, &scmd)<0 || scmd.len<=0) {
|
|
|
- LM_ERR("cannot get the rpc command parameter\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
+ scmd = *cmd;
|
|
|
|
|
|
/* initialize jsonrpc context */
|
|
|
ctx = &_jsonrpc_ctx;
|
|
@@ -1028,6 +1120,27 @@ static int jsonrpc_exec(sip_msg_t* msg, char* cmd, char* s2)
|
|
|
LM_ERR("unsupported jsonrpc version [%.*s]\n", val.len, val.s);
|
|
|
goto send_reply;
|
|
|
}
|
|
|
+ /* reply name */
|
|
|
+ if(rpath!=NULL) {
|
|
|
+ if(rpath->s==NULL || rpath->len<=0) {
|
|
|
+ LM_ERR("empty buffer to store the reply name\n");
|
|
|
+ goto send_reply;
|
|
|
+ }
|
|
|
+ nj = srjson_GetObjectItem(ctx->jreq, ctx->jreq->root, "reply_name");
|
|
|
+ if(nj==NULL) {
|
|
|
+ LM_ERR("missing reply_name field in request\n");
|
|
|
+ goto send_reply;
|
|
|
+ }
|
|
|
+ val.s = nj->valuestring;
|
|
|
+ val.len = strlen(val.s);
|
|
|
+ if(val.len>=rpath->len) {
|
|
|
+ LM_ERR("no space to store reply_name field\n");
|
|
|
+ goto send_reply;
|
|
|
+ }
|
|
|
+ strncpy(rpath->s, val.s, val.len);
|
|
|
+ rpath->s[val.len] = 0;
|
|
|
+ rpath->len = val.len;
|
|
|
+ }
|
|
|
/* run jsonrpc command */
|
|
|
nj = srjson_GetObjectItem(ctx->jreq, ctx->jreq->root, "method");
|
|
|
if(nj==NULL) {
|
|
@@ -1062,6 +1175,16 @@ send_reply:
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+static int jsonrpc_exec(sip_msg_t* msg, char* cmd, char* s2)
|
|
|
+{
|
|
|
+ str scmd;
|
|
|
+
|
|
|
+ if(fixup_get_svalue(msg, (gparam_t*)cmd, &scmd)<0 || scmd.len<=0) {
|
|
|
+ LM_ERR("cannot get the rpc command parameter\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ return jsonrpc_exec_ex(&scmd, NULL);
|
|
|
+}
|
|
|
/**
|
|
|
*
|
|
|
*/
|
|
@@ -1153,3 +1276,446 @@ static int jsonrpc_pv_parse_jrpl_name(pv_spec_t *sp, str *in)
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+/* FIFO TRANSPORT */
|
|
|
+
|
|
|
+/*! \brief Initialize fifo transport */
|
|
|
+static int jsonrpc_init_fifo_file(void)
|
|
|
+{
|
|
|
+ int n;
|
|
|
+ struct stat filestat;
|
|
|
+
|
|
|
+ /* checking the jsonrpc_fifo module param */
|
|
|
+ if (jsonrpc_fifo==NULL || *jsonrpc_fifo == 0) {
|
|
|
+ jsonrpc_fifo=NULL;
|
|
|
+ LM_DBG("No fifo configured\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ LM_DBG("testing if fifo file exists ...\n");
|
|
|
+ n=stat(jsonrpc_fifo, &filestat);
|
|
|
+ if (n==0) {
|
|
|
+ /* FIFO exist, delete it (safer) if no config check */
|
|
|
+ if(config_check==0) {
|
|
|
+ if (unlink(jsonrpc_fifo)<0){
|
|
|
+ LM_ERR("Cannot delete old fifo (%s): %s\n",
|
|
|
+ jsonrpc_fifo, strerror(errno));
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (n<0 && errno!=ENOENT){
|
|
|
+ LM_ERR("MI FIFO stat failed: %s\n", strerror(errno));
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* checking the fifo_reply_dir param */
|
|
|
+ if(!jsonrpc_fifo_reply_dir || *jsonrpc_fifo_reply_dir == 0) {
|
|
|
+ LM_ERR("fifo_reply_dir parameter is empty\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check if the directory for the reply fifo exists */
|
|
|
+ n = stat(jsonrpc_fifo_reply_dir, &filestat);
|
|
|
+ if(n < 0){
|
|
|
+ LM_ERR("Directory stat for MI Fifo reply failed: %s\n", strerror(errno));
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(S_ISDIR(filestat.st_mode) == 0){
|
|
|
+ LM_ERR("fifo_reply_dir parameter is not a directory\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* check fifo_mode */
|
|
|
+ if(!jsonrpc_fifo_mode){
|
|
|
+ LM_WARN("cannot specify fifo_mode = 0, forcing it to rw-------\n");
|
|
|
+ jsonrpc_fifo_mode = S_IRUSR | S_IWUSR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (jsonrpc_fifo_uid_s){
|
|
|
+ if (user2uid(&jsonrpc_fifo_uid, &jsonrpc_fifo_gid, jsonrpc_fifo_uid_s)<0){
|
|
|
+ LM_ERR("Bad user name %s\n", jsonrpc_fifo_uid_s);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (jsonrpc_fifo_gid_s){
|
|
|
+ if (group2gid(&jsonrpc_fifo_gid, jsonrpc_fifo_gid_s)<0){
|
|
|
+ LM_ERR("Bad group name %s\n", jsonrpc_fifo_gid_s);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* add space for one extra process */
|
|
|
+ register_procs(1);
|
|
|
+
|
|
|
+ /* add child to update local config framework structures */
|
|
|
+ cfg_register_child(1);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int jsonrpc_fifo_read = 0;
|
|
|
+static int jsonrpc_fifo_write = 0;
|
|
|
+#define JSONRPC_MAX_FILENAME 128
|
|
|
+static char *jsonrpc_reply_fifo_s = NULL;
|
|
|
+static int jsonrpc_reply_fifo_len = 0;
|
|
|
+
|
|
|
+/*! \brief Initialize Fifo server */
|
|
|
+FILE *jsonrpc_init_fifo_server(char *fifo_name, int fifo_mode,
|
|
|
+ int fifo_uid, int fifo_gid, char* fifo_reply_dir)
|
|
|
+{
|
|
|
+ FILE *fifo_stream;
|
|
|
+ long opt;
|
|
|
+
|
|
|
+ /* create FIFO ... */
|
|
|
+ if ((mkfifo(fifo_name, fifo_mode)<0)) {
|
|
|
+ LM_ERR("Can't create FIFO: %s (mode=%d)\n", strerror(errno), fifo_mode);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ LM_DBG("FIFO created @ %s\n", fifo_name );
|
|
|
+
|
|
|
+ if ((chmod(fifo_name, fifo_mode)<0)) {
|
|
|
+ LM_ERR("Can't chmod FIFO: %s (mode=%d)\n", strerror(errno), fifo_mode);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((fifo_uid!=-1) || (fifo_gid!=-1)){
|
|
|
+ if (chown(fifo_name, fifo_uid, fifo_gid)<0){
|
|
|
+ LM_ERR("Failed to change the owner/group for %s to %d.%d; %s[%d]\n",
|
|
|
+ fifo_name, fifo_uid, fifo_gid, strerror(errno), errno);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ LM_DBG("fifo %s opened, mode=%o\n", fifo_name, fifo_mode );
|
|
|
+
|
|
|
+ /* open it non-blocking or else wait here until someone
|
|
|
+ * opens it for writing */
|
|
|
+ jsonrpc_fifo_read=open(fifo_name, O_RDONLY|O_NONBLOCK, 0);
|
|
|
+ if (jsonrpc_fifo_read<0) {
|
|
|
+ LM_ERR("Can't open fifo %s for reading - fifo_read did not open: %s\n", fifo_name, strerror(errno));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ fifo_stream = fdopen(jsonrpc_fifo_read, "r");
|
|
|
+ if (fifo_stream==NULL) {
|
|
|
+ LM_ERR("fdopen failed on %s: %s\n", fifo_name, strerror(errno));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* make sure the read fifo will not close */
|
|
|
+ jsonrpc_fifo_write=open(fifo_name, O_WRONLY|O_NONBLOCK, 0);
|
|
|
+ if (jsonrpc_fifo_write<0) {
|
|
|
+ LM_ERR("fifo_write did not open: %s\n", strerror(errno));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ /* set read fifo blocking mode */
|
|
|
+ if ((opt=fcntl(jsonrpc_fifo_read, F_GETFL))==-1){
|
|
|
+ LM_ERR("fcntl(F_GETFL) failed: %s [%d]\n", strerror(errno), errno);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if (fcntl(jsonrpc_fifo_read, F_SETFL, opt & (~O_NONBLOCK))==-1){
|
|
|
+ LM_ERR("cntl(F_SETFL) failed: %s [%d]\n", strerror(errno), errno);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ jsonrpc_reply_fifo_s = pkg_malloc(JSONRPC_MAX_FILENAME);
|
|
|
+ if (jsonrpc_reply_fifo_s==NULL) {
|
|
|
+ LM_ERR("no more private memory\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* init fifo reply dir buffer */
|
|
|
+ jsonrpc_reply_fifo_len = strlen(fifo_reply_dir);
|
|
|
+ memcpy( jsonrpc_reply_fifo_s, jsonrpc_fifo_reply_dir, jsonrpc_reply_fifo_len);
|
|
|
+
|
|
|
+ return fifo_stream;
|
|
|
+}
|
|
|
+
|
|
|
+/*! \brief Read input on fifo */
|
|
|
+int jsonrpc_read_stream(char *b, int max, FILE *stream, int *lread)
|
|
|
+{
|
|
|
+ int retry_cnt;
|
|
|
+ int len;
|
|
|
+ char *p;
|
|
|
+ int sstate;
|
|
|
+ int pcount;
|
|
|
+ int pfound;
|
|
|
+ int stype;
|
|
|
+
|
|
|
+ sstate = 0;
|
|
|
+ retry_cnt=0;
|
|
|
+
|
|
|
+ *lread = 0;
|
|
|
+ p = b;
|
|
|
+ pcount = 0;
|
|
|
+ pfound = 0;
|
|
|
+ stype = 0;
|
|
|
+
|
|
|
+ while(1) {
|
|
|
+ len = fread (p, 1, 1, stream);
|
|
|
+ if(len==0) {
|
|
|
+ LM_ERR("fifo server fread failed: %s\n", strerror(errno));
|
|
|
+ /* on Linux, sometimes returns ESPIPE -- give
|
|
|
+ it few more chances
|
|
|
+ */
|
|
|
+ if (errno==ESPIPE) {
|
|
|
+ retry_cnt++;
|
|
|
+ if (retry_cnt>4)
|
|
|
+ return -1;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ /* interrupted by signal or ... */
|
|
|
+ if ((errno==EINTR)||(errno==EAGAIN))
|
|
|
+ continue;
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if(*p=='"' && (sstate==0 || stype==1)) {
|
|
|
+ if(*lread>0) {
|
|
|
+ if(*(p-1)!='\\') {
|
|
|
+ sstate = (sstate+1) % 2;
|
|
|
+ stype = 1;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ sstate = (sstate+1) % 2;
|
|
|
+ stype = 1;
|
|
|
+ }
|
|
|
+ } else if(*p=='\'' && (sstate==0 || stype==2)) {
|
|
|
+ if(*lread>0) {
|
|
|
+ if(*(p-1)!='\\') {
|
|
|
+ sstate = (sstate+1) % 2;
|
|
|
+ stype = 2;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ sstate = (sstate+1) % 2;
|
|
|
+ stype = 2;
|
|
|
+ }
|
|
|
+ } else if(*p=='{') {
|
|
|
+ if(sstate==0) {
|
|
|
+ pfound = 1;
|
|
|
+ pcount++;
|
|
|
+ }
|
|
|
+ } else if(*p=='}') {
|
|
|
+ if(sstate==0)
|
|
|
+ pcount--;
|
|
|
+ }
|
|
|
+ *lread = *lread + 1;
|
|
|
+ if(*lread>=max-1) {
|
|
|
+ LM_WARN("input data too large (%d)\n", *lread);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ p++;
|
|
|
+ if(pfound==1 && pcount==0) {
|
|
|
+ *p = 0;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+/*! \brief reply fifo security checks:
|
|
|
+ *
|
|
|
+ * checks if fd is a fifo, is not hardlinked and it's not a softlink
|
|
|
+ * opened file descriptor + file name (for soft link check)
|
|
|
+ * \return 0 if ok, <0 if not */
|
|
|
+static int jsonrpc_fifo_check(int fd, char* fname)
|
|
|
+{
|
|
|
+ struct stat fst;
|
|
|
+ struct stat lst;
|
|
|
+
|
|
|
+ if (fstat(fd, &fst)<0){
|
|
|
+ LM_ERR("security: fstat on %s failed: %s\n", fname, strerror(errno));
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ /* check if fifo */
|
|
|
+ if (!S_ISFIFO(fst.st_mode)){
|
|
|
+ LM_ERR("security: %s is not a fifo\n", fname);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ /* check if hard-linked */
|
|
|
+ if (fst.st_nlink>1){
|
|
|
+ LM_ERR("security: fifo_check: %s is hard-linked %d times\n", fname, (unsigned)fst.st_nlink);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* lstat to check for soft links */
|
|
|
+ if (lstat(fname, &lst)<0){
|
|
|
+ LM_ERR("security: lstat on %s failed: %s\n", fname, strerror(errno));
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if (S_ISLNK(lst.st_mode)){
|
|
|
+ LM_ERR("security: fifo_check: %s is a soft link\n", fname);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ /* if this is not a symbolic link, check to see if the inode didn't
|
|
|
+ * change to avoid possible sym.link, rm sym.link & replace w/ fifo race
|
|
|
+ */
|
|
|
+ if ((lst.st_dev!=fst.st_dev)||(lst.st_ino!=fst.st_ino)){
|
|
|
+ LM_ERR("security: fifo_check: inode/dev number differ: %d %d (%s)\n",
|
|
|
+ (int)fst.st_ino, (int)lst.st_ino, fname);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ /* success */
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#define JSONRPC_REPLY_RETRIES 4
|
|
|
+FILE *jsonrpc_open_reply_fifo(str *srpath)
|
|
|
+{
|
|
|
+ int retries=JSONRPC_REPLY_RETRIES;
|
|
|
+ int fifofd;
|
|
|
+ FILE *file_handle;
|
|
|
+ int flags;
|
|
|
+
|
|
|
+ if ( memchr(srpath->s, '.', srpath->len)
|
|
|
+ || memchr(srpath->s, '/', srpath->len)
|
|
|
+ || memchr(srpath->s, '\\', srpath->len) ) {
|
|
|
+ LM_ERR("Forbidden reply fifo filename: %.*s\n",
|
|
|
+ srpath->len, srpath->s);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (jsonrpc_reply_fifo_len + srpath->len + 1 > JSONRPC_MAX_FILENAME) {
|
|
|
+ LM_ERR("Reply fifo filename too long %d\n",
|
|
|
+ jsonrpc_reply_fifo_len + srpath->len);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(jsonrpc_reply_fifo_s + jsonrpc_reply_fifo_len, srpath->s, srpath->len);
|
|
|
+ jsonrpc_reply_fifo_s[jsonrpc_reply_fifo_len + srpath->len]=0;
|
|
|
+
|
|
|
+
|
|
|
+tryagain:
|
|
|
+ /* open non-blocking to make sure that a broken client will not
|
|
|
+ * block the FIFO server forever */
|
|
|
+ fifofd=open( jsonrpc_reply_fifo_s, O_WRONLY | O_NONBLOCK );
|
|
|
+ if (fifofd==-1) {
|
|
|
+ /* retry several times if client is not yet ready for getting
|
|
|
+ feedback via a reply pipe
|
|
|
+ */
|
|
|
+ if (errno==ENXIO) {
|
|
|
+ /* give up on the client - we can't afford server blocking */
|
|
|
+ if (retries==0) {
|
|
|
+ LM_ERR("no client at %s\n", jsonrpc_reply_fifo_s );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ /* don't be noisy on the very first try */
|
|
|
+ if (retries != JSONRPC_REPLY_RETRIES)
|
|
|
+ LM_DBG("mi_fifo retry countdown: %d\n", retries );
|
|
|
+ sleep_us( 80000 );
|
|
|
+ retries--;
|
|
|
+ goto tryagain;
|
|
|
+ }
|
|
|
+ /* some other opening error */
|
|
|
+ LM_ERR("open error (%s): %s\n", jsonrpc_reply_fifo_s, strerror(errno));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* security checks: is this really a fifo?, is
|
|
|
+ * it hardlinked? is it a soft link? */
|
|
|
+ if (jsonrpc_fifo_check(fifofd, jsonrpc_reply_fifo_s)<0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ /* we want server blocking for big writes */
|
|
|
+ if ( (flags=fcntl(fifofd, F_GETFL, 0))<0) {
|
|
|
+ LM_ERR("pipe (%s): getfl failed: %s\n", jsonrpc_reply_fifo_s, strerror(errno));
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ flags&=~O_NONBLOCK;
|
|
|
+ if (fcntl(fifofd, F_SETFL, flags)<0) {
|
|
|
+ LM_ERR("pipe (%s): setfl cntl failed: %s\n", jsonrpc_reply_fifo_s, strerror(errno));
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* create an I/O stream */
|
|
|
+ file_handle=fdopen( fifofd, "w");
|
|
|
+ if (file_handle==NULL) {
|
|
|
+ LM_ERR("open error (%s): %s\n",
|
|
|
+ jsonrpc_reply_fifo_s, strerror(errno));
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ return file_handle;
|
|
|
+error:
|
|
|
+ close(fifofd);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#define JSONRPC_BUF_IN_SIZE 4096
|
|
|
+static void jsonrpc_run_fifo_server(FILE *fifo_stream)
|
|
|
+{
|
|
|
+ FILE *reply_stream;
|
|
|
+ char buf_in[JSONRPC_BUF_IN_SIZE];
|
|
|
+ char buf_rpath[128];
|
|
|
+ int lread;
|
|
|
+ str scmd;
|
|
|
+ str srpath;
|
|
|
+ int nw;
|
|
|
+
|
|
|
+ while(1) {
|
|
|
+ /* update the local config framework structures */
|
|
|
+ cfg_update();
|
|
|
+
|
|
|
+ reply_stream = NULL;
|
|
|
+ lread = 0;
|
|
|
+ if(jsonrpc_read_stream(buf_in, JSONRPC_BUF_IN_SIZE,
|
|
|
+ fifo_stream, &lread)<0 || lread<=0) {
|
|
|
+ LM_DBG("failed to get the json document from fifo stream\n");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ scmd.s = buf_in;
|
|
|
+ scmd.len = lread;
|
|
|
+ trim(&scmd);
|
|
|
+ LM_DBG("preparing to execute fifo jsonrpc [%.*s]\n", scmd.len, scmd.s);
|
|
|
+ srpath.s = buf_rpath;
|
|
|
+ srpath.len = 128;
|
|
|
+ if(jsonrpc_exec_ex(&scmd, &srpath)<0) {
|
|
|
+ LM_ERR("failed to execute the json document from fifo stream\n");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ LM_DBG("command executed - result: [%.*s] [%d] [%p] [%.*s]\n",
|
|
|
+ srpath.len, srpath.s,
|
|
|
+ _jsonrpc_plain_reply.rcode,
|
|
|
+ _jsonrpc_plain_reply.rbody.s,
|
|
|
+ _jsonrpc_plain_reply.rbody.len, _jsonrpc_plain_reply.rbody.s);
|
|
|
+ if(srpath.len>0) {
|
|
|
+ reply_stream = jsonrpc_open_reply_fifo(&srpath);
|
|
|
+ if (reply_stream==NULL) {
|
|
|
+ LM_ERR("cannot open reply fifo: %.*s\n", srpath.len, srpath.s);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ nw = fwrite(_jsonrpc_plain_reply.rbody.s, 1,
|
|
|
+ _jsonrpc_plain_reply.rbody.len, reply_stream);
|
|
|
+ if(nw < _jsonrpc_plain_reply.rbody.len) {
|
|
|
+ LM_ERR("failed to write the reply to fifo: %d out of %d\n",
|
|
|
+ nw, _jsonrpc_plain_reply.rbody.len);
|
|
|
+ }
|
|
|
+ fclose(reply_stream);
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+static void jsonrpc_fifo_process(int rank)
|
|
|
+{
|
|
|
+ FILE *fifo_stream;
|
|
|
+
|
|
|
+ LM_DBG("new process with pid = %d created\n",getpid());
|
|
|
+
|
|
|
+ fifo_stream = jsonrpc_init_fifo_server( jsonrpc_fifo, jsonrpc_fifo_mode,
|
|
|
+ jsonrpc_fifo_uid, jsonrpc_fifo_gid, jsonrpc_fifo_reply_dir);
|
|
|
+ if ( fifo_stream==NULL ) {
|
|
|
+ LM_CRIT("The function jsonrpc_init_fifo_server returned with error!!!\n");
|
|
|
+ exit(-1);
|
|
|
+ }
|
|
|
+
|
|
|
+ jsonrpc_run_fifo_server( fifo_stream );
|
|
|
+
|
|
|
+ LM_CRIT("the function jsonroc_fifo_server returned with error!!!\n");
|
|
|
+ exit(-1);
|
|
|
+}
|