/* * $Id: extcmd_funcs.c,v 1.15 2004/08/24 08:58:29 janakj Exp $ * * * Copyright (C) 2001-2003 FhG Fokus * * 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: * info@iptel.org * * 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 * * History: * -------- * 2003-01-23: switched from t_uac to t_uac_dlg, adapted to new way of * parsing for Content-Type (bogdan) * 2003-08-05: adapted to the new parse_content_type_hdr function (bogdan) * 2003-09-11: updated to new build_lump_rpl() interface (bogdan) * 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan) */ #include #include #include #include #include #include #include #include "extcmd_funcs.h" #include "clients.h" #include "../../error.h" #include "../../str.h" #include "../../ip_addr.h" #include "../../data_lump_rpl.h" #include "../../mem/shm_mem.h" #include "../../parser/parse_content.h" #include "../../parser/parse_from.h" #include "../tm/tm_load.h" #define NO_CMD 0 #define MSG_CMD 1 #define RPL_CMD 2 #define BYE_CMD 3 #define FTH_CMD 4 #define VOID_CMD 5 #define INV_CMD 6 #define ACK_CMD 7 #define BUFFER_SIZE 2048 #define HEADER_SIZE 5 #define TYPE_LEN 1 #define LEN_LEN 4 #define append_str(_p,_s,_l) \ {memcpy((_p),(_s),(_l));\ (_p) += (_l);} typedef struct anchor_struct { int fd; str cmd; } anchor_t; /* global variable */ struct tm_binds tmb; int rpl_pipe[2]; int req_pipe[2]; inline int get_int_n2h( char *b, int l) { int n,i; for(i=0,n=0;i>((l-i-1)*8)) & 0x000000FF); } #if 0 inline int extcmd_add_contact(struct sip_msg* msg , str* to_uri) { struct lump_rpl *lump; char *buf, *p; int len; len = 9 /*"Contact: "*/ + to_uri->len + 2/*"<>"*/ + CRLF_LEN; buf = pkg_malloc( len ); if(!buf) { LOG(L_ERR,"ERROR:extcmd_add_contact: out of memory! \n"); return -1; } p = buf; append_str( p, "Contact: " , 9); *(p++) = '<'; append_str( p, to_uri->s, to_uri->len); *(p++) = '>'; append_str( p, CRLF, CRLF_LEN); lump = build_lump_rpl( buf , len , LUMP_RPL_HDR); if(!lump) { LOG(L_ERR,"ERROR:extcmd_add_contact: unable to build lump_rpl! \n"); pkg_free( buf ); return -1; } add_lump_rpl( msg , lump ); pkg_free(buf); return 1; } #endif int dump_request(struct sip_msg *msg, char *para1, char *para2) { anchor_t anchor; str body; struct to_body *from; char *cmd; int cmd_len; char *p; int mime; /* get the message's body * anyhow we have to call this function, so let's do it at the beginning * to force the parsing of all the headers - like this we avoid separate * calls of parse_headers function for FROM, CONTENT_LENGTH, TO hdrs */ body.s = get_body( msg ); if (body.s==0) { LOG(L_ERR,"ERROR:extcmd:dump_msg: cannot extract body from msg!\n"); goto error; } /* content-length (if present) must be already parsed */ if (!msg->content_length) { LOG(L_ERR,"ERROR:extcmd:dump_msg: no Content-Length header found!\n"); goto error; } body.len = get_content_length( msg ); /* look for TO header */ if (!msg->to) { LOG(L_ERR,"ERROR:extcmd:dump_msg: no TO header found!\n"); goto error; } /* parse the content-type header */ if ((mime=parse_content_type_hdr(msg))<1 ) { LOG(L_ERR,"ERROR:extcmd:dump_msg:cannot parse Content-Type header\n"); goto error; } /* check the content-type value */ if ( mime!=(TYPE_TEXT<<16)+SUBTYPE_PLAIN && mime!=(TYPE_MESSAGE<<16)+SUBTYPE_CPIM ) { LOG(L_ERR,"ERROR:extcmd:dump_msg: invalid content-type for a " "message request! type found=%d\n",mime); goto error; } if ( parse_from_header(msg)==-1 ) { LOG(L_ERR,"ERROR:extcmd:dump_msg: cannot parse FROM header!\n"); goto error; } from = (struct to_body*)msg->from->parsed; #if 0 /* adds contact header into reply */ if (extcmd_add_contact(msg,&(get_to(msg)->uri))==-1) { LOG(L_ERR,"ERROR:extcmd:dump_msg: can't build contact for reply\n"); goto error; } #endif /*-------------BUILD AND FILL THE COMMAND --------------------*/ /* computes the amount of memory needed */ cmd_len = HEADER_SIZE + /* commnad header */ + 4 + body.len /*body + size */ + 4 + from->uri.len /* from + size */ + 4 + get_to(msg)->uri.len /* to + size */ + 4 + 0; /* extra_headers + size */ /* allocs a chunk of shm memory */ cmd = (char*)shm_malloc( cmd_len ); if (!cmd) { LOG(L_ERR,"ERROR:extcmd:dump_msg: cannot get shm memory!\n"); goto error; } /* start filling the commnad (in revert order, from end)*/ p = cmd + cmd_len; /* copy body's body */ p = p - body.len; memcpy( p, body.s, body.len); /* put body's lengh */ p = p-4; put_int_h2n( body.len, p, 4); /* copy hdr's body - nothing for the moment*/ /* put hdr's lengh */ p = p-4; put_int_h2n( 0, p, 4); /* copy to's body */ p = p - get_to(msg)->uri.len; memcpy( p, get_to(msg)->uri.s, get_to(msg)->uri.len); /* put to's lengh */ p = p-4; put_int_h2n( get_to(msg)->uri.len, p, 4); /* copy from's body */ p = p - from->uri.len; memcpy( p, from->uri.s, from->uri.len); /* put from's lengh */ p = p-4; put_int_h2n( from->uri.len, p, 4); /* add the cmd size */ p = p-4; put_int_h2n( cmd_len-HEADER_SIZE, p, 4); /* add the cmd type */ p = p-1; put_int_h2n( MSG_CMD, p, 1); /* fill in the anchor */ anchor.fd = 0; anchor.cmd.s = cmd; anchor.cmd.len = cmd_len; /* send the anchor through pipe to the extcmd server process */ DBG("DEBUG:extcmd:dump_msg: posting request in pipe!\n"); if (write( req_pipe[1], &anchor, sizeof(anchor))!=sizeof(anchor) ) { LOG(L_ERR,"ERROR:extcmd:dump_msg: cannot write to request pipe" " : %s\n",strerror(errno) ); goto error; } return 1; error: return -1; } static int push_reply_to_client(int code, char* reason_s, int reason_l, int client_fd) { anchor_t anchor; str reply; char *p; /* compose the reply commnad that will be sent back to the client */ reply.len = 1/*type*/+4/*cmd_size*/+4/*rpl_code*/+4/*rpl_reason_size*/+ reason_l/*rpl_reason_string*/; reply.s = (char*) shm_malloc( reply.len ); if (reply.s==0) { LOG(L_ERR,"ERROR::extcmd:tuac_callback: no more shm_mem free!\n"); return -1; } /* fill up the rpl_cmd - in revert order (from end)*/ p = reply.s + reply.len; /* add reason string */ p = p-reason_l; memcpy( p, reason_s, reason_l); /* add reason string len */ p = p-4; put_int_h2n( reason_l, p, 4); /* add the rpl code */ p = p-4; put_int_h2n( code, p, 4); /* add the cmd size */ p = p-4; put_int_h2n( 4+4+reason_l, p, 4); /* add the cmd type */ p = p-1; put_int_h2n( RPL_CMD, p, 1); /* push reply on rpl_pipe */ anchor.fd = client_fd; anchor.cmd.s = reply.s; anchor.cmd.len = reply.len; if (write( rpl_pipe[1] , &anchor, sizeof(anchor) )!=sizeof(anchor)) { LOG(L_ERR,"ERROR::extcmd:push_reply_to_client: cannot write " "to rpl_pipe : Reason: %s !\n",strerror(errno) ); return -1; } return 1; } void tuac_callback( struct cell *t, struct sip_msg *msg, int code, void *param) { str reason; DBG("DEBUG:extcmd:tuac_callback: reply status=%d\n", code); if(!t->cbp) { LOG(L_ERR,"ERROR:extcmd:tuac_callback: parameter not received\n"); return; } /* get the status text for this reply code */ get_reply_status( &reason, msg, code); push_reply_to_client( code, reason.s, reason.len, *((int*)t->cbp)); if (reason.s) pkg_free(reason.s); } int send_sip_req(str* msg_type, str *msg, int client_fd) { char err_buf[256]; str to; str from; str hdrs; str body; int *pcbp; char *p; char *end; int len; int ret; int err_ret; int sip_error; /* split the msg into from, to hdrs, body */ end = msg->s + msg->len; p = msg->s; /* get from len */ len = get_int_n2h( p, 4); p = p + 4; if ( p+len>end) { LOG(L_ERR,"ERROR:extcmd:send_sip_req: FROM size to big (%d)\n",len); push_reply_to_client(400,"extcmd: invalid FROM header",27,client_fd); goto error; } /* get from body */ from.s = p; from.len = len; p += len; DBG("DEBUG:extcmd:send_sip_req: from=%d<%.*s>\n",from.len,from.len,from.s); /* get to len */ len = get_int_n2h( p, 4); p = p + 4; if ( p+len>end) { LOG(L_ERR,"ERROR:extcmd:send_sip_req: TO size to big (%d)\n",len); push_reply_to_client(400,"extcmd: invalid TO header",25,client_fd); goto error; } /* get to body */ to.s = p; to.len = len; p += len; DBG("DEBUG:extcmd:send_sip_req: to=%d<%.*s>\n",to.len,to.len,to.s); /* get headers len */ len = get_int_n2h( p, 4); p = p + 4; if ( p+len>end) { LOG(L_ERR,"ERROR:extcmd:send_sip_req: HDRS size to big (%d)\n",len); push_reply_to_client(400,"extcmd: invalid HEADERS field",29,client_fd); goto error; } /* get headers body */ hdrs.s = p; hdrs.len = len; p += len; DBG("DEBUG:extcmd:send_sip_req: hdrs=%d<%.*s>\n",hdrs.len,hdrs.len,hdrs.s); /* get body len */ len = get_int_n2h( p, 4); p = p + 4; if ( p+len>end) { LOG(L_ERR,"ERROR:extcmd:send_sip_req: BODY size to big (%d)\n",len); push_reply_to_client(400,"extcmd: invalid BODY field",26,client_fd); goto error; } /* get body's body */ body.s = p; body.len = len; DBG("DEBUG:extcmd:send_sip_req: body=%d<%.*s>\n",body.len,body.len,body.s); /* allocate the param in shm */ if( !(pcbp = (int*)shm_malloc(sizeof(int*))) ) { LOG(L_ERR,"ERROR:extcmd:send_sip_req: no more shm mem free!!\n"); push_reply_to_client(500,"extcmd: no shm memory free",26,client_fd); goto error; } *pcbp = client_fd; /* send the message */ ret = tmb.t_request( msg_type, /* request type */ 0, /* Request-URI */ &to, /* To */ &from, /* From */ &hdrs, /* Additional headers including CRLF */ &body, /* Message body */ tuac_callback, /* Callback function */ (void*)pcbp /* Callback parameter */ ); if (ret<=0) { err_ret=err2reason_phrase(ret, &sip_error, err_buf, sizeof(err_buf), "EXTCMD" ) ; if (err_ret > 0 ) { push_reply_to_client( sip_error, err_buf, err_ret, client_fd); } else { push_reply_to_client(500,"EXTCMD unknown error\n",20,client_fd); } } return 1; error: return -1; } inline int forward_reply_to_client(int pipe_fd) { anchor_t anchor; anchor.cmd.s = 0; anchor.cmd.len = 0; /* get cmd from pipe */ if (read(pipe_fd,&anchor,sizeof(anchor))!=sizeof(anchor) ) { LOG(L_ERR,"ERROR:extcmd:send_cmd_from_pipe_to_client: cannot read from" " pipe: %s \n", strerror(errno)); goto error; } /* send the reply */ if (write( anchor.fd, anchor.cmd.s, anchor.cmd.len)!=anchor.cmd.len ) { LOG(L_ERR,"ERROR:extcmd:send_cmd_from_pipe_to_client: cannot write to " "socket: %s \n", strerror(errno)); goto error; } /* free the mem */ shm_free( anchor.cmd.s ); return 1; error: if (anchor.cmd.s) shm_free( anchor.cmd.s ); return -1; } static int forward_request_to_all_clients(int pipe_fd) { anchor_t anchor; client_t *client; int fd; int i; anchor.cmd.s = 0; anchor.cmd.len = 0; /* get request cmd from pipe */ if (read(pipe_fd,&anchor,sizeof(anchor))!=sizeof(anchor) ) { LOG(L_ERR,"ERROR:extcmd:send_cmd_from_pipe_to_client: cannot read from" " pipe: %s \n", strerror(errno)); goto error; } /* send the reply to all clients */ for(i=0; ifd; if (write( fd, anchor.cmd.s, anchor.cmd.len)!=anchor.cmd.len ) { LOG(L_ERR,"ERROR:extcmd:forward_request_to_all_client: cannot " "write to socket: %s \n", strerror(errno)); continue; } } /* free the mem */ shm_free( anchor.cmd.s ); return 1; error: if (anchor.cmd.s) shm_free( anchor.cmd.s ); return -1; } inline int read_n( int fd, int n, char *b) { int l; int c; l=0; c=0; while( l0 ) l += c; if (c<0) return c; return l; } static int get_cmd( int fd, str *cmd) { static char buffer[BUFFER_SIZE]; int cmd_type; int cmd_len; int len; /* get command header */ len = read_n( fd, HEADER_SIZE , buffer); if (lens = buffer; cmd->len = cmd_len; return cmd_type; } #if 0 static int send_void_command( int sock_fd ) { char buf[HEADER_SIZE]; /* add command type */ put_int_h2n( VOID_CMD, buf, 1); /* add the cmd len */ put_int_h2n( 0, buf+1, 4); /* send the reply */ if (write( sock_fd, buf, HEADER_SIZE)!=HEADER_SIZE ) { LOG(L_ERR,"ERROR:extcmd:send_void_commnad: cannot write to " "socket: %s \n", strerror(errno)); return -1; } return 1; } #endif inline void FD_SET_AND_MAX( fd_set *fdset, int fd, int *max_fd) { FD_SET( fd, fdset ); if (fd>*max_fd) *max_fd = fd; } inline void FD_CLR_AND_MAX( fd_set *fdset, int fd, int *max_fd) { int i; int new_max = 0; FD_CLR( fd, fdset ); if (fd==*max_fd) { for(i=0;i<=*max_fd;i++) if ( FD_ISSET(i,fdset) && i>new_max ) new_max = i; *max_fd = new_max; } } void extcmd_server_process( int server_sock ) { str message_req = { "MESSAGE", 7}; //str invite_req = { "INVITE", 6}; fd_set read_set; fd_set wait_set; client_t *client; union sockaddr_union sau; int sau_len; str cmd; int cmd_type; int max_fd; int fd = -1; int index; /* set SIG_PIPE to be ignored by this process */ if (signal( SIGPIPE, SIG_IGN)==SIG_ERR) { LOG(L_ERR,"ERROR:extcmd_server_process: cannot set SIGPIPE to be " "ignored! \n"); goto error; } /* set the socket for listening */ if ( listen( server_sock, 10)==-1 ) { LOG(L_ERR,"ERROR:extcmd_server_process: cannot listen to the server" " socket: %s \n", strerror(errno) ); goto error; } sau_len = sizeof( union sockaddr_union ); /* prepare fd set for select */ FD_ZERO( &wait_set ); max_fd = 0; FD_SET_AND_MAX( &wait_set, server_sock, &max_fd); FD_SET_AND_MAX( &wait_set, req_pipe[0], &max_fd); FD_SET_AND_MAX( &wait_set, rpl_pipe[0], &max_fd); while(1) { /* what fd should we listen to? */ read_set = wait_set; if (clients_is_full()) FD_CLR_AND_MAX( &read_set, server_sock, &max_fd); /* wait for read something */ DBG("DEBUG:extcmd_server_process: waiting to read!\n"); if (select( max_fd+1, &read_set, 0, 0, 0)==-1) { LOG(L_ERR,"ERROR:extcmd_server_process: select failed : %s\n", strerror(errno) ); sleep(1); continue; } /* let's see what we read */ /* maybe is a connect request !?*/ if ( FD_ISSET(server_sock, &read_set) ) { fd=accept(server_sock,(struct sockaddr*)&sau,(socklen_t*)&sau_len); if (fd==-1) { LOG(L_ERR,"ERROR: extcmd_server_process: accept failed : %s\n", strerror(errno) ); } else { DBG("DEBUG:extcmd_server_process: connection accepted \n"); add_client(fd); /* add this socket to wait_sock to read from it */ FD_SET_AND_MAX( &wait_set, fd, &max_fd); } } /* maybe is a from req_pipe ?! */ if ( FD_ISSET(req_pipe[0], &read_set) ) { forward_request_to_all_clients( req_pipe[0] ); } /* maybe is a SIP reply from rpl_pipe ?! */ if ( FD_ISSET(rpl_pipe[0], &read_set) ) { forward_reply_to_client( rpl_pipe[0] ); } /* maybe is from a client -> look for it's fd */ for( fd=-1,index=0 ; indexfd,&read_set) ) continue; /* we can read something from this client */ fd = client->fd; /* get the command from the client */ cmd_type = get_cmd(fd,&cmd); switch (cmd_type) { /* NO commnad - nothing was read - probably disconnection */ case NO_CMD : /* BYE command - used by the client to end the connection */ case BYE_CMD: DBG("DEBUG:extcmd_server_process: BYE received->close\n"); /* remove client's fd from wait_sock set */ FD_CLR_AND_MAX( &wait_set, fd, &max_fd); /* remove client */ del_client(index); index--; /* close the connection */ close(fd); break; /* MSG commnad - client wants to send a SIP message request */ case MSG_CMD: DBG("DEBUG:extcmd_server_process: MSG received\n"); /* send the message */ send_sip_req( &message_req, &cmd, fd); break; /* unknown commnad was received - ignore it */ default: LOG(L_ERR,"ERROR:extcmd_server_process: unknown command " "[%d] received from client (fd=%d)\n",cmd_type,fd); } /* end switch */ } /* end for */ } error: return; }