datagram_fnc.c

Go to the documentation of this file.
00001 /*
00002  * $Id: datagram_fnc.c 1133 2007-04-02 17:31:13Z ancuta_onofrei $
00003  *
00004  * Copyright (C) 2007 Voice Sistem SRL
00005  *
00006  * This file is part of Kamailio, a free SIP server.
00007  *
00008  * Kamailio is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * Kamailio is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00021  *
00022  *
00023  * History:
00024  * ---------
00025  *  2007-06-25  first version (ancuta)
00026  */
00027 
00028 /*!
00029  * \file
00030  * \brief MI_DATAGRAM :: Datagram functions
00031  * \ingroup mi
00032  */
00033 
00034 
00035 #include <arpa/inet.h>
00036 #include <errno.h>
00037 #include <fcntl.h>
00038 #include <netinet/in.h>
00039 #include <netdb.h>
00040 #include <stdlib.h>
00041 #include <stdio.h>
00042 #include <string.h>
00043 #include <sys/un.h>
00044 #include <sys/socket.h>
00045 #include <sys/stat.h>
00046 #include <sys/types.h>
00047 #include <unistd.h>
00048 #include <signal.h>
00049 
00050 #include "../../resolve.h"
00051 #include "mi_datagram.h"
00052 #include "datagram_fnc.h"
00053 #include "mi_datagram_parser.h"
00054 #include "mi_datagram_writer.h"
00055 
00056 /* solaris doesn't have SUN_LEN  */
00057 #ifndef SUN_LEN
00058 #define SUN_LEN(sa)   ( strlen((sa)->sun_path) + \
00059                 (size_t)(((struct sockaddr_un*)0)->sun_path) )
00060 #endif
00061 /* AF_LOCAL is not defined on solaris */
00062 #if !defined(AF_LOCAL)
00063 #define AF_LOCAL AF_UNIX
00064 #endif
00065 
00066 int flags;
00067 static char *mi_buf = 0;
00068 
00069 static union {
00070    struct sockaddr_un un;
00071    struct sockaddr_in in;
00072 } reply_addr;
00073 
00074 static unsigned int reply_addr_len;
00075 
00076 extern int mi_socket_timeout;    /*!< Timeout for sending replies in milliseconds */
00077 static unsigned int mi_socket_domain;
00078 
00079 static int mi_sock_check(int fd, char* fname);
00080 
00081 #define mi_create_dtgram_replysocket(_socketfd,_socket_domain, _err) \
00082    _socketfd = socket(_socket_domain, SOCK_DGRAM, 0);\
00083    if (_socketfd == -1) {\
00084       LM_ERR("cannot create socket: %s\n", strerror(errno));\
00085       goto _err;\
00086    }\
00087    /* Turn non-blocking mode on for tx*/\
00088    flags = fcntl(_socketfd, F_GETFL);\
00089    if (flags == -1){\
00090       LM_ERR("fcntl failed: %s\n", strerror(errno));\
00091       goto _err;\
00092    }\
00093    if (fcntl(_socketfd, F_SETFL, flags | O_NONBLOCK) == -1) {\
00094       LM_ERR("fcntl: set non-blocking failed: %s\n", strerror(errno));\
00095       goto _err;\
00096    }
00097 
00098 
00099 int  mi_init_datagram_server(sockaddr_dtgram *addr, unsigned int socket_domain, rx_tx_sockets * socks, int mode, int uid, int gid )
00100 {
00101    char * socket_name;
00102 
00103    /* create sockets rx and tx ... */
00104    /***********************************/
00105    mi_socket_domain = socket_domain;
00106    /**********************************/
00107 
00108    socks->rx_sock = socket(socket_domain, SOCK_DGRAM, 0);
00109    if (socks->rx_sock == -1) {
00110       LM_ERR("cannot create RX socket: %s\n", strerror(errno));
00111       return -1;
00112    }
00113 
00114    switch(socket_domain)
00115    {
00116    case AF_LOCAL:
00117          LM_DBG("we have a unix socket: %s\n", addr->unix_addr.sun_path);
00118          socket_name = addr->unix_addr.sun_path;
00119          if(bind(socks->rx_sock,(struct sockaddr*)&addr->unix_addr, SUN_LEN(&addr->unix_addr))< 0) {
00120             LM_ERR("bind: %s\n", strerror(errno));
00121             goto err_rx;
00122          }
00123          if(mi_sock_check(socks->rx_sock, socket_name)!=0)
00124             goto err_rx;
00125          /* change permissions */
00126          if (mode){
00127             if (chmod(socket_name, mode)<0){
00128                LM_ERR("failed to change the permissions for %s to %04o:"
00129                   "%s[%d]\n",socket_name, mode, strerror(errno), errno);
00130                goto err_rx;
00131             }
00132          }
00133          /* change ownership */
00134          if ((uid!=-1) || (gid!=-1)){
00135             if (chown(socket_name, uid, gid)<0){
00136                LM_ERR("failed to change the owner/group for %s  to %d.%d;"
00137                "%s[%d]\n",socket_name, uid, gid, strerror(errno), errno);
00138                goto err_rx;
00139             }
00140          }
00141          break;
00142 
00143    case AF_INET:
00144          if (bind(socks->rx_sock, &addr->udp_addr.s,
00145          sockaddru_len(addr->udp_addr))< 0) {
00146             LM_ERR("bind: %s\n", strerror(errno));
00147             goto err_rx;
00148          }
00149          break;
00150 #ifdef USE_IPV6
00151    case AF_INET6: 
00152          if(bind(socks->rx_sock, (struct sockaddr*)&addr->udp_addr.sin6, sizeof(addr->udp_addr)) < 0) {
00153             LM_ERR("bind: %s\n", strerror(errno));
00154             goto err_rx;
00155          }
00156          break;
00157 #endif
00158    default:
00159          LM_ERR("domain not supported\n");
00160          goto err_both;
00161 
00162    }
00163    mi_create_dtgram_replysocket(socks->tx_sock,socket_domain, err_both);
00164 
00165    return 0;
00166 err_both:
00167    close(socks->tx_sock);
00168 err_rx:
00169    close(socks->rx_sock);
00170    return -1;
00171 }
00172 
00173 
00174 
00175 int mi_init_datagram_buffer(void) {
00176 
00177    mi_buf = pkg_malloc(DATAGRAM_SOCK_BUF_SIZE);
00178    if ( mi_buf==NULL) {
00179       LM_ERR("no more pkg memory\n");
00180       return -1;
00181    }
00182    return 0;
00183 }
00184 
00185 /*! \brief reply socket security checks:
00186  * checks if fd is a socket, is not hardlinked and it's not a softlink
00187  * opened file descriptor + file name (for soft link check)
00188  * \return 0 if ok, <0 if not */
00189 int mi_sock_check(int fd, char* fname)
00190 {
00191    struct stat fst;
00192    struct stat lst;
00193 
00194    if (fstat(fd, &fst)<0){
00195       LM_ERR("fstat failed: %s\n", 
00196       strerror(errno));
00197       return -1;
00198    }
00199    /* check if socket */
00200    if (!S_ISSOCK(fst.st_mode)){
00201       LM_ERR("%s is not a sock\n", fname);
00202       return -1;
00203    }
00204    /* check if hard-linked */
00205    if (fst.st_nlink>1){
00206       LM_ERR("security: sock_check: %s is hard-linked %d times\n", 
00207             fname, (unsigned)fst.st_nlink);
00208       return -1;
00209    }
00210 
00211    /* lstat to check for soft links */
00212    if (lstat(fname, &lst)<0){
00213       LM_ERR("lstat failed: %s\n", strerror(errno));
00214       return -1;
00215    }
00216    if (S_ISLNK(lst.st_mode)){
00217       LM_ERR("security: sock_check: %s is a soft link\n", fname);
00218       return -1;
00219    }
00220    /* if this is not a symbolic link, check to see if the inode didn't
00221     * change to avoid possible sym.link, rm sym.link & replace w/ sock race
00222     */
00223    /*LM_DBG("for %s lst.st_dev %fl fst.st_dev %i lst.st_ino %i fst.st_ino"
00224       "%i\n", fname, lst.st_dev, fst.st_dev, lst.st_ino, fst.st_ino);*/
00225    /*if ((lst.st_dev!=fst.st_dev)||(lst.st_ino!=fst.st_ino)){
00226       LM_ERR("security: sock_check: "
00227          "socket  %s inode/dev number differ: %d %d \n", fname,
00228          (int)fst.st_ino, (int)lst.st_ino);
00229    }*/
00230    /* success */
00231    return 0;
00232 }
00233 
00234 
00235 
00236 /* this function sends the reply over the reply socket */
00237 static int mi_send_dgram(int fd, char* buf, unsigned int len, 
00238             const struct sockaddr* to, int tolen, int timeout)
00239 {
00240    int n;
00241    size_t total_len;
00242    total_len = strlen(buf);
00243 
00244    /*LM_DBG("response is %s \n tolen is %i "
00245          "and len is %i\n",buf,  tolen,len);*/
00246 
00247    if(total_len == 0 || tolen ==0)
00248       return -1;
00249    
00250    if (total_len>DATAGRAM_SOCK_BUF_SIZE) {
00251       LM_DBG("datagram too big, trunking, datagram_size is %i\n", DATAGRAM_SOCK_BUF_SIZE);
00252       len = DATAGRAM_SOCK_BUF_SIZE;
00253    }
00254    /*LM_DBG("destination address length is %i\n", tolen);*/
00255    n=sendto(fd, buf, len, 0, to, tolen);
00256    return n;
00257 }
00258 
00259 
00260 
00261 /*function that verifyes that the function from the datagram's first 
00262  * line is correct and exists*/
00263 static int identify_command(datagram_stream * dtgram, struct mi_cmd * *f)
00264 {
00265    char *command,*p, *start;
00266 
00267    /* default offset for the command: 0 */
00268    p= dtgram->start;
00269    start = p;
00270    if (!p){
00271       LM_ERR("null pointer  \n");
00272       return -1;
00273    }
00274    
00275    /*if no command*/
00276    if ( dtgram->len ==0 ){
00277       LM_DBG("command empty case1 \n");
00278       goto error;
00279    }
00280    if (*p != MI_CMD_SEPARATOR){
00281       LM_ERR("command must begin with: %c \n", MI_CMD_SEPARATOR);
00282       goto error;
00283    }
00284    command = p+1;
00285 
00286    LM_DBG("the command starts here: %s\n", command);
00287    p=strchr(command, MI_CMD_SEPARATOR );
00288    if (!p ){
00289       LM_ERR("empty command \n");
00290       goto error;
00291    }
00292    if(*(p+1)!='\n'){
00293       LM_ERR("the request's first line is invalid :no newline after "
00294             "the second %c\n",MI_CMD_SEPARATOR);
00295       goto error;
00296    }
00297 
00298    /* make command zero-terminated */
00299    *p=0;
00300    LM_DBG("the command is %s\n",command);
00301    /*search for the appropiate command*/
00302    *f=lookup_mi_cmd( command, p-command);
00303    if(!*f)
00304       goto error;
00305    /*the current offset has changed*/
00306    LM_DBG("dtgram->len is %i\n", dtgram->len);
00307    dtgram->current = p+2 ;
00308    dtgram->len -=p+2 - dtgram->start;
00309    LM_DBG("dtgram->len is %i\n",dtgram->len);
00310 
00311    return 0;
00312 error:
00313    return -1;
00314 }
00315 
00316 
00317 /*************************** async functions ******************************/
00318 static inline void free_async_handler( struct mi_handler *hdl )
00319 {
00320    if (hdl)
00321       shm_free(hdl);
00322 }
00323 
00324 
00325 static void datagram_close_async(struct mi_root *mi_rpl,struct mi_handler *hdl, int done)
00326 {
00327    datagram_stream dtgram;
00328    int ret;
00329    my_socket_address *p;
00330    int reply_sock, flags;
00331 
00332    p = (my_socket_address *)hdl->param;
00333 
00334    LM_DBG("the socket domain is %i and af_local is %i\n", p->domain, AF_LOCAL);
00335 
00336    mi_create_dtgram_replysocket(reply_sock, p->domain, err);
00337 
00338 
00339    if (mi_rpl!=0) {
00340       /*allocate the response datagram*/  
00341       dtgram.start = pkg_malloc(DATAGRAM_SOCK_BUF_SIZE);
00342       if(!dtgram.start) {
00343          LM_ERR("no more pkg memory\n");
00344          goto err;
00345       }
00346       /*build the response*/
00347       if(mi_datagram_write_tree(&dtgram , mi_rpl) != 0) {
00348          LM_ERR("failed to build the response \n");   
00349          goto err;
00350       }
00351       LM_DBG("the response is %s", dtgram.start);
00352 
00353       /*send the response*/
00354       ret = mi_send_dgram(reply_sock, dtgram.start, dtgram.current - dtgram.start, 
00355          (struct sockaddr *)&p->address, p->address_len, mi_socket_timeout);
00356       if (ret>0) {
00357          LM_DBG("the response: %s has been sent in %i octets\n", dtgram.start, ret);
00358       } else {
00359          LM_ERR("failed to send the response, ret is %i\n",ret);
00360       }
00361       free_mi_tree(mi_rpl);
00362       pkg_free(dtgram.start);
00363    } else if (done) {
00364       mi_send_dgram(reply_sock, MI_COMMAND_FAILED, MI_COMMAND_FAILED_LEN,
00365             (struct sockaddr*)&reply_addr, reply_addr_len, mi_socket_timeout);
00366       free_async_handler( hdl );
00367    }
00368 
00369    close(reply_sock);
00370    return;
00371 
00372 err:
00373    if(dtgram.start)
00374       pkg_free(dtgram.start);
00375    close(reply_sock);
00376    return;
00377 }
00378 
00379 
00380 
00381 static inline struct mi_handler *build_async_handler(unsigned int sock_domain,
00382          struct sockaddr *reply_addr, unsigned int reply_addr_len)
00383 {
00384    struct mi_handler *hdl;
00385    void *p;
00386    my_socket_address *repl_address;
00387 
00388    hdl = (struct mi_handler*)shm_malloc( sizeof(struct mi_handler) + sizeof(my_socket_address));
00389    if (hdl==0) {
00390       LM_ERR("no more shm mem\n");
00391       return 0;
00392    }
00393 
00394    p = (void *)((hdl) + 1);
00395    repl_address = p;
00396    
00397    switch(sock_domain)
00398    {/*we can have either of these types of sockets*/
00399       case AF_LOCAL: LM_DBG("we have an unix socket\n");
00400             memcpy(&repl_address->address.unix_deb, reply_addr, reply_addr_len);
00401             break;
00402       case AF_INET:  LM_DBG("we have an IPv4 socket\n");
00403             memcpy(&repl_address->address.inet_v4, reply_addr, reply_addr_len);
00404             break;
00405       case AF_INET6: LM_DBG("we have an IPv6 socket\n");
00406             memcpy(&repl_address->address.inet_v6, reply_addr, reply_addr_len);
00407             break;
00408       default: LM_CRIT("socket_domain has an incorrect value\n");
00409             shm_free(hdl);
00410             return NULL;
00411    }
00412    repl_address->domain = sock_domain;
00413    repl_address->address_len  = reply_addr_len;
00414 
00415    hdl->handler_f = datagram_close_async;
00416    hdl->param = (void*)repl_address;
00417 
00418    return hdl;
00419 }
00420 
00421 
00422 
00423 void mi_datagram_server(int rx_sock, int tx_sock)
00424 {
00425    struct mi_root *mi_cmd;
00426    struct mi_root *mi_rpl;
00427    struct mi_handler *hdl;
00428    struct mi_cmd * f;
00429    datagram_stream dtgram;
00430 
00431    int ret, len;
00432 
00433    ret = 0;
00434    f = 0;
00435 
00436    while(1) { /*read the datagram*/
00437       memset(mi_buf, 0, DATAGRAM_SOCK_BUF_SIZE);
00438       reply_addr_len = sizeof(reply_addr);
00439 
00440       /* get the client's address */
00441       ret = recvfrom(rx_sock, mi_buf, DATAGRAM_SOCK_BUF_SIZE, 0, 
00442             (struct sockaddr*)&reply_addr, &reply_addr_len);
00443 
00444       if (ret == -1) {
00445          LM_ERR("recvfrom: (%d) %s\n", errno, strerror(errno));
00446          if ((errno == EINTR) ||
00447             (errno == EAGAIN) ||
00448             (errno == EWOULDBLOCK) ||
00449             (errno == ECONNREFUSED)) {
00450             LM_DBG("got %d (%s), going on\n", errno, strerror(errno));
00451             continue;
00452          }
00453          LM_DBG("error in recvfrom\n");
00454          continue;
00455       }
00456 
00457       if(ret == 0)
00458          continue;
00459 
00460       LM_DBG("received %.*s\n", ret, mi_buf);
00461 
00462       if(ret> DATAGRAM_SOCK_BUF_SIZE) {
00463          LM_ERR("buffer overflow\n");
00464          continue;
00465       }
00466 
00467       LM_DBG("mi_buf is %s and we have received %i bytes\n",mi_buf, ret);
00468       dtgram.start = mi_buf;
00469       dtgram.len= ret;
00470       dtgram.current = dtgram.start;
00471 
00472       ret = identify_command(&dtgram, &f);
00473       /*analyze the command--from the first line*/
00474       if(ret != 0) {
00475          LM_ERR("command not available\n");
00476          mi_send_dgram(tx_sock, MI_COMMAND_NOT_AVAILABLE, 
00477                     MI_COMMAND_AVAILABLE_LEN, 
00478                     (struct sockaddr* )&reply_addr, reply_addr_len, 
00479                     mi_socket_timeout);
00480          continue;
00481       }
00482       LM_DBG("we have a valid command \n");
00483 
00484       /* if asyncron cmd, build the async handler */
00485       if (f->flags&MI_ASYNC_RPL_FLAG) {
00486          hdl = build_async_handler(mi_socket_domain,
00487                (struct sockaddr* )&reply_addr, reply_addr_len);
00488          if (hdl==0) {
00489             LM_ERR("failed to build async handler\n");
00490             mi_send_dgram(tx_sock, MI_INTERNAL_ERROR,
00491                   MI_INTERNAL_ERROR_LEN,(struct sockaddr* )&reply_addr,
00492                   reply_addr_len, mi_socket_timeout);
00493             continue;
00494          }
00495       } else {
00496          hdl = 0;
00497       }
00498 
00499       LM_DBG("after identifing the command, the received datagram is  %s\n", dtgram.current);
00500 
00501       /*if no params required*/
00502       if (f->flags&MI_NO_INPUT_FLAG) {
00503          LM_DBG("the command has no params\n");
00504          mi_cmd = 0;
00505       } else {
00506          LM_DBG("parsing the command's params\n");
00507          mi_cmd = mi_datagram_parse_tree(&dtgram);
00508          if (mi_cmd==NULL){
00509             LM_ERR("failed to parse the MI tree\n");
00510             mi_send_dgram(tx_sock, MI_PARSE_ERROR, MI_PARSE_ERROR_LEN,
00511                     (struct sockaddr* )&reply_addr, reply_addr_len,
00512                     mi_socket_timeout);
00513             free_async_handler(hdl);
00514             continue;
00515          }
00516          mi_cmd->async_hdl = hdl;
00517       }
00518 
00519       LM_DBG("done parsing the mi tree\n");
00520       if ( (mi_rpl=run_mi_cmd(f, mi_cmd))==0 ) {
00521          /*error while running the command*/
00522          LM_ERR("failed to process the command\n");
00523          mi_send_dgram(tx_sock, MI_COMMAND_FAILED, MI_COMMAND_FAILED_LEN,
00524                (struct sockaddr* )&reply_addr, reply_addr_len, 
00525                mi_socket_timeout);
00526          goto failure;
00527       }
00528 
00529       /*the command exited well*/
00530       LM_DBG("command process (%s)succeded\n",f->name.s); 
00531 
00532       if (mi_rpl!=MI_ROOT_ASYNC_RPL) {
00533          if(mi_datagram_write_tree(&dtgram , mi_rpl) != 0){
00534             LM_ERR("failed to build the response \n");   
00535             goto failure;
00536          }
00537 
00538          len = dtgram.current - dtgram.start;
00539          ret = mi_send_dgram(tx_sock, dtgram.start,len, 
00540                   (struct sockaddr* )&reply_addr, 
00541                   reply_addr_len, mi_socket_timeout);
00542          if (ret>0){
00543             LM_DBG("the response: %s has been sent in %i octets\n", dtgram.start, ret);
00544          } else {
00545             LM_ERR("failed to send the response\n");
00546          }
00547          free_mi_tree( mi_rpl );
00548          free_async_handler(hdl);
00549          if (mi_cmd)
00550             free_mi_tree( mi_cmd );
00551       } else { 
00552          if (mi_cmd)
00553             free_mi_tree( mi_cmd );
00554       }
00555 
00556       continue;
00557 
00558 failure:
00559       free_async_handler(hdl);
00560       /* destroy request tree */
00561       if (mi_cmd)
00562          free_mi_tree( mi_cmd );
00563       /* destroy the reply tree */
00564       if (mi_rpl)
00565          free_mi_tree(mi_rpl);
00566       continue;
00567    }
00568 }
00569 

Generated on Mon May 21 18:00:25 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6