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.
sems/AmServer.cpp

475 lines
12 KiB

/*
* $Id: AmServer.cpp,v 1.26.2.2 2005/08/25 06:55:12 rco Exp $
*
* Copyright (C) 2002-2003 Fhg Fokus
*
* This file is part of sems, a free SIP media server.
*
* sems 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:
* info@iptel.org
*
* sems 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 "AmServer.h"
#include "log.h"
#include "AmRequest.h"
#include "AmUtils.h"
#include "AmConfig.h"
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
#define READ_PARAMETER_FIFO(p)\
READ_FIFO_PARAMETER(fifo_stream,p,line_buf,MAX_LINE_SIZE)
#define READ_PARAMETER_UN(p)\
{\
msg_get_param(msg_c,p);\
DBG("%s= <%s>\n",#p,p.c_str());\
}
AmFifoServer* AmFifoServer::_instance;
AmFifoServer* AmFifoServer::instance()
{
if(!_instance)
_instance = new AmFifoServer();
return _instance;
}
AmFifoServer::AmFifoServer()
: fifo_stream(NULL)
{
}
AmFifoServer::~AmFifoServer()
{
if(fifo_stream)
fclose(fifo_stream);
}
int AmFifoServer::get_line(char* str, size_t len)
{
return fifo_get_line(fifo_stream,str,len);
}
int AmFifoServer::get_lines(char* str, size_t len)
{
return fifo_get_lines(fifo_stream,str,len);
}
void AmFifoServer::consume_request()
{
char line_buf[MAX_LINE_SIZE];
while( get_line(line_buf,MAX_LINE_SIZE) != 0 ) {
ERROR("Consumed: %s\n",line_buf);
}
}
char* AmFifoServer::get_next_tok(char*& str)
{
char* start=str;
while(*start==' ') start++;
char* s=start;
while(*s){
if(*s == ' '){
*s='\0';
str=s+1;
return start;
}
s++;
}
str = s;
return ( s > start ? start : 0 );
}
int child_pid=0;
int is_main=1;
int AmFifoServer::init(const char * fifo_name)
{
// create FIFO file
if( (mkfifo(fifo_name,FIFO_PERM)<0) && (errno!=EEXIST) ) {
ERROR("while creating fifo `%s': %s \n",fifo_name,strerror(errno));
return -1;
}
// avoid the FIFO reaches EOF...
if((child_pid=fork())>0){
FILE* fifo_write=0;
if(!(fifo_write=fopen(fifo_name, "w"))){
ERROR("while opening fifo `%s': %s\n",fifo_name,strerror(errno));
kill(child_pid,SIGTERM);
}
waitpid(child_pid,0,0);
if(fifo_write)
fclose(fifo_write);
return -1;
}
// the main process is waiting
// for that child to terminate
is_main = 0;
if( !(fifo_stream = fopen(fifo_name,"r")) ) {
ERROR("while opening fifo `%s': %s\n",fifo_name,strerror(errno));
return -1;
}
return 0;
}
void AmFifoServer::run()
{
INFO("FIFO server started\n");
char line_buf[MAX_LINE_SIZE];
string version;
string fct_name;
string cmd;
string::size_type pos;
while(true){
try {
READ_PARAMETER_FIFO(version);
if (version == "") {
// some odd trailer -- ignore
ERROR("odd trailer\n");
continue;
}
if(version != FIFO_VERSION){
throw string("wrong FIFO Interface version:"+version);
}
READ_PARAMETER_FIFO(fct_name);
if( ((pos = fct_name.find('.')) == string::npos)
// || (pos+1 >= fct_name.length())
)
throw string("bad command: '" + fct_name + "'. "
"Syntax: fct_name.{plugin_name,'bye'}");
cmd = fct_name.substr(pos+1,string::npos);
fct_name = fct_name.substr(0,pos);
if(fct_name != "sip_request"){
map<string,AmFifoServerFct*>::iterator fct_it;
if( (fct_it = fct_map.find(fct_name)) == fct_map.end() )
throw string("unknown server function: '" + fct_name + "'");
fct_it->second->execute(fifo_stream,cmd);
continue;
}
else
execute(fifo_stream,cmd);
}
catch(const string& err_msg){
ERROR("%s\n",err_msg.c_str());
consume_request();
continue;
}
}
}
int AmFifoServer::registerFct(const string& name,AmFifoServerFct* fct)
{
if(fct_map.find(name) != fct_map.end()){
ERROR("AmFifoServer::registerFct: function '%s'"
" has already been registered.\n",name.c_str());
return -1;
}
fct_map[name] = fct;
DBG("AmFifoServer::registerFct: function '%s'"
" has been registered.\n",name.c_str());
return 0;
}
int AmFifoServer::execute(FILE* stream, const string& cmd_str)
{
char line_buf[MAX_LINE_SIZE];
char body_buf[MSG_BODY_SIZE];
char hdrs_buf[MSG_BODY_SIZE];
int body_len=0, hdrs_len=0;
AmCmd cmd;
if(cmd_str.empty())
throw string("AmServer::execute: FIFO parameter plug-in name missing.");
cmd.cmd = cmd_str;
READ_PARAMETER_FIFO(cmd.method);
READ_PARAMETER_FIFO(cmd.user);
READ_PARAMETER_FIFO(cmd.domain);
READ_PARAMETER_FIFO(cmd.dstip); // will be taken for UDP's local peer IP & Contact
READ_PARAMETER_FIFO(cmd.port); // will be taken for Contact
READ_PARAMETER_FIFO(cmd.r_uri); // ??? will be taken for the answer's Contact header field ???
READ_PARAMETER_FIFO(cmd.from_uri); // will be taken for subsequent request uri
READ_PARAMETER_FIFO(cmd.from);
READ_PARAMETER_FIFO(cmd.to);
READ_PARAMETER_FIFO(cmd.callid);
READ_PARAMETER_FIFO(cmd.from_tag);
READ_PARAMETER_FIFO(cmd.to_tag);
string cseq_str;
READ_PARAMETER_FIFO(cseq_str);
if(sscanf(cseq_str.c_str(),"%u", &cmd.cseq) != 1){
throw string("invalid cseq number (") + cseq_str + string(")\n");
}
DBG("cseq= <%s>(%i)\n",cseq_str.c_str(),cmd.cseq);
READ_PARAMETER_FIFO(cmd.key);
READ_PARAMETER_FIFO(cmd.route);
READ_PARAMETER_FIFO(cmd.next_hop);
if( (hdrs_len = get_lines(hdrs_buf,MSG_BODY_SIZE)) == -1 ) {
throw string("len(hdrs) > ") + int2str(MSG_BODY_SIZE) + "\n";
}
hdrs_buf[hdrs_len]='\0';
DBG("hdrs: `%s'\n",hdrs_buf);
cmd.hdrs = hdrs_buf;
if( (body_len = get_lines(body_buf,MSG_BODY_SIZE)) == -1 ) {
throw string("len(body) > ") + int2str(MSG_BODY_SIZE) + "\n";
}
body_buf[body_len]='\0';
DBG("body: `%.*s'\n",body_len,body_buf);
#define IS_EMPTY(p) if(p.empty()) \
throw string("invalid FIFO command: " #p " is empty !!!" );
IS_EMPTY(cmd.cmd);
IS_EMPTY(cmd.from);
IS_EMPTY(cmd.to);
IS_EMPTY(cmd.callid);
IS_EMPTY(cmd.from_tag);
#undef IS_EMPTY
DBG("everything is OK !\n");
AmRequestUAS req(cmd,body_buf);
req.execute();
return 0;
}
AmUnServer* AmUnServer::_instance;
AmUnServer* AmUnServer::instance()
{
if(!_instance)
_instance = new AmUnServer();
return _instance;
}
AmUnServer::AmUnServer()
: fifo_socket(-1)
{
}
AmUnServer::~AmUnServer()
{
if(fifo_socket != -1){
close(fifo_socket);
}
}
void AmUnServer::on_stop()
{
}
int AmUnServer::init(const char * sock_name)
{
unlink(sock_name);
fifo_socket = create_unix_socket(sock_name);
if(fifo_socket == -1){
ERROR("could not create server socket: exiting\n");
return -1;
}
return 0;
}
void AmUnServer::run()
{
INFO("Unix socket server started\n");
char msg_buf[MSG_BUFFER_SIZE];
char* msg_c;
int err_cnt = 0;
int msg_sz;
string version;
string fct_name;
string cmd;
string::size_type pos;
while(true){
try {
msg_sz = recv(fifo_socket,msg_buf,MSG_BUFFER_SIZE,MSG_TRUNC);
if(msg_sz == -1){
ERROR("recv on server socket failed: %s\n",strerror(errno));
if(++err_cnt >= MAX_MSG_ERR){
ERROR("too many consecutive errors: exiting now!\n");
return;
}
continue;
}
else
err_cnt=0;
if(msg_sz > MSG_BUFFER_SIZE){
ERROR("server request is too big (size=%i): discarding\n",msg_sz);
continue;
}
msg_buf[msg_sz-1] = '\0';
msg_c = msg_buf;
DBG("recv-ed: <%s>\n",msg_buf);
READ_PARAMETER_UN(version);
if(version != FIFO_VERSION){
throw string("wrong FIFO Interface version.");
}
READ_PARAMETER_UN(fct_name);
if( ((pos = fct_name.find('.')) == string::npos)
// || (pos+1 >= fct_name.length())
)
throw string("bad command: '"+fct_name+"'. "
"Syntax: fct_name.{plugin_name,'bye'}");
cmd = fct_name.substr(pos+1,string::npos);
fct_name = fct_name.substr(0,pos);
if(fct_name != "sip_request"){
map<string,AmUnServerFct*>::iterator fct_it;
if( (fct_it = fct_map.find(fct_name)) == fct_map.end() )
throw string("unknown server function: '" + fct_name + "'");
fct_it->second->execute(msg_c,cmd);
continue;
}
else
execute(msg_c,cmd);
}
catch(const string& err_msg){
ERROR("%s\n",err_msg.c_str());
continue;
}
}
}
int AmUnServer::registerFct(const string& name,AmUnServerFct* fct)
{
if(fct_map.find(name) != fct_map.end()){
ERROR("AmUnServer::registerFct: function '%s'"
" has already been registered.\n",name.c_str());
return -1;
}
fct_map[name] = fct;
DBG("AmUnServer::registerFct: function '%s'"
" has been registered.\n",name.c_str());
return 0;
}
int AmUnServer::execute(char* msg_c, const string& cmd_str)
{
char body_buf[MSG_BODY_SIZE];
char hdrs_buf[MSG_BODY_SIZE];
int body_len=0, hdrs_len=0;
AmCmd cmd;
if(cmd_str.empty())
throw string("AmUnServer::execute: FIFO parameter plug-in name missing.");
cmd.cmd = cmd_str;
READ_PARAMETER_UN(cmd.method);
READ_PARAMETER_UN(cmd.user);
//READ_PARAMETER_UN(cmd.email);
READ_PARAMETER_UN(cmd.domain);
READ_PARAMETER_UN(cmd.dstip); // will be taken for UDP's local peer IP & Contact
READ_PARAMETER_UN(cmd.port); // will be taken for Contact
READ_PARAMETER_UN(cmd.r_uri); // ??? will be taken for the answer's Contact header field ???
READ_PARAMETER_UN(cmd.from_uri); // will be taken for subsequent request uri
READ_PARAMETER_UN(cmd.from);
READ_PARAMETER_UN(cmd.to);
READ_PARAMETER_UN(cmd.callid);
READ_PARAMETER_UN(cmd.from_tag);
READ_PARAMETER_UN(cmd.to_tag);
string cseq_str;
READ_PARAMETER_UN(cseq_str);
if(sscanf(cseq_str.c_str(),"%u", &cmd.cseq) != 1){
throw string("invalid cseq number (") + cseq_str + string(")\n");
}
DBG("cseq= <%s>(%i)\n",cseq_str.c_str(),cmd.cseq);
READ_PARAMETER_UN(cmd.key);
READ_PARAMETER_UN(cmd.route);
READ_PARAMETER_UN(cmd.next_hop);
if( (hdrs_len = msg_get_lines(msg_c,hdrs_buf,MSG_BODY_SIZE)) == -1 ) {
throw string("len(hdrs) > ") + int2str(MSG_BODY_SIZE) + "\n";
}
hdrs_buf[hdrs_len]='\0';
DBG("hdrs: `%s'\n",hdrs_buf);
cmd.hdrs = hdrs_buf;
if( (body_len = msg_get_lines(msg_c,body_buf,MSG_BODY_SIZE)) == -1 ) {
throw string("len(body) > ") + int2str(MSG_BODY_SIZE) + "\n";
}
body_buf[body_len]='\0';
DBG("body: `%.*s'\n",body_len,body_buf);
#define IS_EMPTY(p) if(p.empty()) \
throw string("invalid FIFO command: " #p " is empty !!!" );
IS_EMPTY(cmd.cmd);
IS_EMPTY(cmd.from);
IS_EMPTY(cmd.to);
IS_EMPTY(cmd.callid);
IS_EMPTY(cmd.from_tag);
#undef IS_EMPTY
DBG("everything is OK !\n");
AmRequestUAS req(cmd,body_buf);
req.execute();
return 0;
}