eagi_proxy.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk EAGI -> TCP/IP proxy
00003  *    by Danijel Korzinek (devil_slayer _at_ hotmail.com)
00004  *
00005  * This simple C application allows you to control asterisk thru one TCP/IP
00006  * socket and listen to the conversation thru another socket.
00007  *
00008  * Great for ASR or wizzard-of-oz telephony systems!
00009  *
00010  * HOWTO:
00011  *    The program is compiled using the following command:
00012  *       gcc eagi_proxy.c -o eagi_proxy -lpthread
00013  *
00014  *    In the dialplan, you can add something like this to the main context:
00015  *       exten => s,1,Answer
00016  *       exten => s,n,EAGI(/path/to/eagi_proxy)
00017  *       exten => s,n,Hangup
00018  *
00019  *    To test the program you can use the netcat utility:
00020  *       (http://netcat.sourceforge.net/)
00021  *
00022  *       -in one console run:
00023  *          nc -vv -l -p 8418 > /path/to/file.raw
00024  *       -in another run:
00025  *          nc -vv -l -p 8417
00026  *       -you can use any file for the signal or even /dev/null
00027  *       -you can change the port numbers in the sourcecode below
00028  *
00029  *    Once you make the call, both programs will accept the incoming
00030  *    connection. The program on port 8417 will print out the enviornemnt
00031  *    (unless the appropriate define below is commented) and you can write
00032  *    any AGI command there (http://www.voip-info.org/wiki-Asterisk+AGI),
00033  *    e.g.:
00034  *       GET DATA /path/to/gsm/file 10000 4
00035  *
00036  *    Finally, you can open the RAW file in any sound editor. The format is:
00037  *       RAW little-endian 8kHz 16bit
00038  */
00039 
00040 #include <stdlib.h>
00041 #include <unistd.h>
00042 #include <stdio.h>
00043 #include <netinet/in.h>
00044 #include <netinet/tcp.h>
00045 #include <string.h>
00046 #include <time.h>
00047 #include <sys/types.h>
00048 #include <sys/stat.h>
00049 #include <sys/socket.h>
00050 #include <sys/time.h>
00051 #include <arpa/inet.h>
00052 #include <netdb.h>
00053 #include <fcntl.h>
00054 #include <errno.h>
00055 #include <ctype.h>
00056 #include <pthread.h>
00057 
00058 /* DEFINES */
00059 #define SIGNAL_PORT 8418
00060 #define COMMAND_PORT 8417
00061 #define SEND_ENVIORNMENT /*send the enviornment thru the socket*/
00062 /************************/
00063 
00064 
00065 #define BUFSIZE 1024
00066 char buf[BUFSIZE];
00067 
00068 #define WINSIZE 400 /* 25 ms @ 8 kHz and 16bit */
00069 char window[WINSIZE];
00070 
00071 #define WINBUF_NUM 2400 /* number of WINSIZE windows = 1 minute */
00072 char* winbuf;
00073 char *end, *bs, *be;
00074 /* winbuf - start of buffer 
00075  * end - end of buffer  
00076  * bs - start of data
00077  * be - end of data 
00078  */
00079 
00080 int command_desc; /* command transfer descriptor */
00081 int speech_desc; /* speech signal descrriptor */
00082 char connected=1; /* connection state */
00083 
00084 int connect_to_host(char* host, int port); /* connect to a given host (name or IP) and given port number in nonblocking mode returning socket descriptor*/
00085 
00086 void read_full(int file, char* buffer, int num); /* read EXACTLY "num" ammount of bytes from "file" descriptor to "buffer"*/
00087 int read_some(int file, char* buffer, int size); /* read AT MOST "size" ammount of bytes */
00088 
00089 void write_buf(int file, char* buffer, int num); /* write "num" ammount of bytes to "file" descriptor and buffer the surplus if the write would block */
00090 int write_amap(int file, char* buffer, int num); /*write AT MOST "num" ammount of bytes and return ammount that was written*/
00091 
00092 void setnonblocking(int desc); /*sets the socket non-blocking; for polling */
00093 
00094 void finalize(); /* this function is run at exit */
00095 
00096 pthread_mutex_t command_mutex;/* command socket mutex */
00097 pthread_t stdin_thread,signal_thread;
00098 void* readStdin(void* ptr);
00099 void* readSignal(void* ptr);
00100 
00101 /* The program creates 3 threads:
00102  * 1) Main thread - reads commands from the socket and sends them to asterisk
00103  * 2) stdin_thread - reads asterisk output and sends it to the command socket
00104  * 3) signal_thread - reads the sound from asterisk and sends it to the signal socket
00105  */
00106 
00107 int main()
00108 {
00109    int ret;
00110    
00111    atexit(finalize);
00112 
00113    setlinebuf(stdin);
00114    setlinebuf(stdout);
00115 
00116    winbuf=(char*)malloc(WINSIZE*WINBUF_NUM);
00117    end=winbuf+WINSIZE*WINBUF_NUM;
00118    bs=be=winbuf;
00119 
00120    speech_desc=connect_to_host("localhost",SIGNAL_PORT);
00121    if(speech_desc<0) 
00122    {
00123       perror("signal socket");
00124       return -1;
00125    }
00126 
00127 
00128    command_desc=connect_to_host("localhost",COMMAND_PORT);
00129    if(command_desc<0) 
00130    {
00131       perror("command socket");
00132       return -1;
00133    }
00134 
00135    pthread_mutex_init(&command_mutex,NULL);
00136    pthread_create(&stdin_thread,NULL,readStdin,NULL);
00137    pthread_create(&signal_thread,NULL,readSignal,NULL);
00138 
00139    while(connected)
00140    {  
00141       pthread_mutex_lock(&command_mutex);
00142       ret=read_some(command_desc,buf,BUFSIZE);
00143       pthread_mutex_unlock(&command_mutex);
00144       if(ret>0)
00145       {
00146          buf[ret]=0;
00147          printf("%s",buf);       
00148       }
00149    }
00150 
00151    return 0;
00152 }
00153 
00154 void finalize()
00155 {
00156    close(command_desc);
00157    close(speech_desc);
00158    free(winbuf);  
00159 }
00160 
00161 void* readStdin(void* ptr)
00162 {
00163    while(1)/*read enviornment*/
00164    {
00165       fgets(buf,BUFSIZE,stdin);
00166       #ifdef SEND_ENVIORNMENT
00167          pthread_mutex_lock(&command_mutex);
00168          write_buf(command_desc,buf,strlen(buf));
00169          pthread_mutex_unlock(&command_mutex);
00170       #endif
00171       if(feof(stdin) || buf[0]=='\n') 
00172       {
00173          break;
00174       }
00175    }
00176 
00177    while(connected)
00178    {
00179       fgets(buf,BUFSIZE,stdin);
00180       pthread_mutex_lock(&command_mutex);
00181       write_buf(command_desc,buf,strlen(buf));
00182       pthread_mutex_unlock(&command_mutex);
00183    }
00184 
00185    pthread_exit(NULL);
00186 }
00187 
00188 void* readSignal(void* ptr)
00189 {
00190    while(connected)
00191    {
00192       read_full(3,window,WINSIZE);
00193       write_buf(speech_desc,window,WINSIZE);
00194    }
00195 
00196    pthread_exit(NULL);
00197 }
00198 
00199 
00200 void read_full(int file, char* buffer, int num)
00201 {
00202    int count,pos=0;
00203 
00204    while(num)
00205    {
00206       count=read(file,buffer+pos,num);
00207       if(count==0 || (count<0 && errno!=EAGAIN))
00208       {
00209          connected=0;
00210          return;
00211       }
00212       num-=count;
00213       pos+=count;
00214    }
00215 }
00216 
00217 int connect_to_host(char* name, int port)
00218 {
00219    int address;
00220    struct hostent* host_entity;
00221    int res,desc;
00222    int opts;
00223    struct sockaddr_in host;
00224    
00225 
00226    /* get address */ 
00227    if(!strcmp(name,"localhost"))
00228       address=htonl(2130706433); /*127.0.0.1*/
00229    else
00230    {
00231       address=inet_addr(name); /* check if it's an IP that's written in the string */
00232       if(address==(in_addr_t)-1)
00233       {
00234          host_entity = gethostbyname(name); /* search for the host under this name */
00235    
00236          if(!host_entity)
00237          {
00238             fprintf(stderr,"EAGI proxy: Wrong address!\n"); /* can't find anything*/
00239             return -1;
00240          }
00241          address=*((int*)host_entity->h_addr);
00242       }
00243    }
00244 
00245    desc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
00246    if(desc<0)
00247    {
00248       fprintf(stderr,"EAGI proxy: Cannot create socket!\n");
00249       return -1;
00250    }
00251 
00252    memset((void*)&host,0,sizeof(struct sockaddr_in));
00253    
00254    host.sin_family=AF_INET;
00255    host.sin_port=htons(port);
00256    host.sin_addr.s_addr=address;
00257    
00258    res=connect(desc,(struct sockaddr*)&host,sizeof(host));
00259    if(res<0)
00260    {
00261       fprintf(stderr,"EAGI proxy: Cannot connect!\n");
00262       return -1;
00263    }
00264 
00265    /* set to non-blocking mode */
00266    opts = fcntl(desc,F_GETFL);
00267    if (opts < 0) {
00268       perror("fcntl(F_GETFL)");
00269       exit(EXIT_FAILURE);
00270    }
00271    opts = (opts | O_NONBLOCK);
00272    if (fcntl(desc,F_SETFL,opts) < 0) {
00273       perror("fcntl(F_SETFL)");
00274       exit(EXIT_FAILURE);
00275    }
00276 
00277 
00278    return desc;
00279 }
00280 
00281 int read_some(int desc, char* buffer, int size)
00282 {
00283    unsigned char c;
00284    int res,i=0;
00285 
00286    for(;;)
00287    {
00288       res=read(desc,&c,1);
00289       if(res<1)
00290       {
00291          if(errno!=EAGAIN)
00292          {
00293             perror("Error reading");
00294             connected=0;
00295          }  
00296          break;
00297       }
00298       if(res==0) 
00299       {
00300          connected=0;
00301          break;
00302       }
00303       
00304       buffer[i]=c;
00305       i++;
00306    }
00307 
00308    return i;
00309 }
00310 
00311 /* This is a tricky function! */
00312 void write_buf(int desc, char* buf, int size)
00313 {
00314    int ret;
00315 
00316    /*NOTE: AMAP -> as much as possible */
00317 
00318    if(be!=bs)/* if data left in buffer */
00319    {
00320       if(be>bs)/* if buffer not split */
00321       {
00322          ret=write_amap(desc,bs,be-bs);/* write AMAP */
00323          bs+=ret;/* shift the start of the buffer */
00324       }
00325       else/* if buffer is split */
00326       {
00327          ret=write_amap(desc,bs,end-bs);/* write higher part first */
00328          if(ret==end-bs)/* if wrote whole of the higher part */
00329          {
00330             ret=write_amap(desc,winbuf,be-winbuf);/* write lower part */
00331             bs=winbuf+ret;/* shift start to new position */
00332          }
00333          else bs+=ret;/* if not wrote whole of higher part, only shift start */
00334       }
00335    }
00336 
00337    if(be==bs)/* if buffer is empty now */
00338    {
00339       ret=write_amap(desc,buf,size);/* write AMAP of the new data */
00340       buf+=ret;/* shift start of new data */
00341       size-=ret;/* lower size of new data */
00342    }
00343 
00344    if(size)/* if new data still remains unsent */
00345    {
00346       if(be>=bs)/* if data not split */
00347       {
00348          if(size>end-be)/* if new data size doesn't fit higher end */
00349          {
00350             size-=end-be;/* reduce new data size by the higher end size */
00351             memcpy(be,buf,end-be);/* copy to higher end */
00352             be=winbuf;/* shift end to begining of buffer */
00353             buf+=end-be;/* shift start of new data */
00354          }
00355          else/* if new data fits the higher end */
00356          {
00357             memcpy(be,buf,size);/* copy to higher end */
00358             be+=size;/* shift end by size */
00359             if(be>=end)/* if end goes beyond the buffer */
00360                be=winbuf;/* restart */
00361             size=0;/* everything copied */
00362          }
00363       }
00364 
00365       if(size)/* if new data still remains */
00366       {
00367          if(size>=bs-be)/* if new data doesn't fit between end and start */
00368          {
00369             fprintf(stderr,"Buffer overflow!\n");
00370             size=bs-be-1;/* reduce the size that we can copy */
00371          }
00372 
00373          if(size)/* if we can copy anything */
00374          {
00375             memcpy(be,buf,size);/* copy the new data between end and start */
00376             be+=size;/* shift end by size */
00377          }
00378       }
00379    }
00380 }
00381 
00382 int write_amap(int desc, char* buf, int size)
00383 {
00384    int ret;
00385    ret=write(desc,buf,size);
00386    if(ret<0)
00387    {
00388       if(errno!=EAGAIN) 
00389       {
00390          perror("Error writing");
00391          connected=0;
00392       }
00393       return 0;
00394    }
00395    if(ret==0)
00396       connected=0;
00397 
00398    return ret;
00399 }
00400 
00401 
00402 void setnonblocking(int desc)
00403 {
00404    int opts;
00405    
00406    opts = fcntl(desc,F_GETFL);
00407    if(opts < 0)
00408    {
00409       perror("fcntl(F_GETFL)");
00410       exit(-1);
00411    }
00412 
00413    opts = (opts | O_NONBLOCK );
00414    if(fcntl(desc,F_SETFL,opts) < 0)
00415    {
00416       perror("fcntl(F_SETFL)");
00417       exit(-1);
00418    }
00419 }

Generated on Thu Apr 16 06:27:33 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6