123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- /*
- * Asterisk EAGI -> TCP/IP proxy
- * by Danijel Korzinek (devil_slayer _at_ hotmail.com)
- *
- * This simple C application allows you to control asterisk thru one TCP/IP
- * socket and listen to the conversation thru another socket.
- *
- * Great for ASR or wizzard-of-oz telephony systems!
- *
- * HOWTO:
- * The program is compiled using the following command:
- * gcc eagi_proxy.c -o eagi_proxy -lpthread
- *
- * In the dialplan, you can add something like this to the main context:
- * exten => s,1,Answer
- * exten => s,n,EAGI(/path/to/eagi_proxy)
- * exten => s,n,Hangup
- *
- * To test the program you can use the netcat utility:
- * (http://netcat.sourceforge.net/)
- *
- * -in one console run:
- * nc -vv -l -p 8418 > /path/to/file.raw
- * -in another run:
- * nc -vv -l -p 8417
- * -you can use any file for the signal or even /dev/null
- * -you can change the port numbers in the sourcecode below
- *
- * Once you make the call, both programs will accept the incoming
- * connection. The program on port 8417 will print out the enviornemnt
- * (unless the appropriate define below is commented) and you can write
- * any AGI command there (http://www.voip-info.org/wiki-Asterisk+AGI),
- * e.g.:
- * GET DATA /path/to/gsm/file 10000 4
- *
- * Finally, you can open the RAW file in any sound editor. The format is:
- * RAW little-endian 8kHz 16bit
- */
- #include <stdlib.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <netinet/in.h>
- #include <netinet/tcp.h>
- #include <string.h>
- #include <time.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/socket.h>
- #include <sys/time.h>
- #include <arpa/inet.h>
- #include <netdb.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <ctype.h>
- #include <pthread.h>
- /* DEFINES */
- #define SIGNAL_PORT 8418
- #define COMMAND_PORT 8417
- #define SEND_ENVIORNMENT /*send the enviornment thru the socket*/
- /************************/
- #define BUFSIZE 1024
- char buf[BUFSIZE];
- #define WINSIZE 400 /* 25 ms @ 8 kHz and 16bit */
- char window[WINSIZE];
- #define WINBUF_NUM 2400 /* number of WINSIZE windows = 1 minute */
- char* winbuf;
- char *end, *bs, *be;
- /* winbuf - start of buffer
- * end - end of buffer
- * bs - start of data
- * be - end of data
- */
- int command_desc; /* command transfer descriptor */
- int speech_desc; /* speech signal descrriptor */
- char connected=1; /* connection state */
- 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*/
- void read_full(int file, char* buffer, int num); /* read EXACTLY "num" ammount of bytes from "file" descriptor to "buffer"*/
- int read_some(int file, char* buffer, int size); /* read AT MOST "size" ammount of bytes */
- 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 */
- int write_amap(int file, char* buffer, int num); /*write AT MOST "num" ammount of bytes and return ammount that was written*/
- void setnonblocking(int desc); /*sets the socket non-blocking; for polling */
- void finalize(); /* this function is run at exit */
- pthread_mutex_t command_mutex;/* command socket mutex */
- pthread_t stdin_thread,signal_thread;
- void* readStdin(void* ptr);
- void* readSignal(void* ptr);
- /* The program creates 3 threads:
- * 1) Main thread - reads commands from the socket and sends them to asterisk
- * 2) stdin_thread - reads asterisk output and sends it to the command socket
- * 3) signal_thread - reads the sound from asterisk and sends it to the signal socket
- */
- int main()
- {
- int ret;
- atexit(finalize);
- setlinebuf(stdin);
- setlinebuf(stdout);
- winbuf=(char*)malloc(WINSIZE*WINBUF_NUM);
- end=winbuf+WINSIZE*WINBUF_NUM;
- bs=be=winbuf;
- speech_desc=connect_to_host("localhost",SIGNAL_PORT);
- if(speech_desc<0)
- {
- perror("signal socket");
- return -1;
- }
- command_desc=connect_to_host("localhost",COMMAND_PORT);
- if(command_desc<0)
- {
- perror("command socket");
- return -1;
- }
- pthread_mutex_init(&command_mutex,NULL);
- pthread_create(&stdin_thread,NULL,readStdin,NULL);
- pthread_create(&signal_thread,NULL,readSignal,NULL);
- while(connected)
- {
- pthread_mutex_lock(&command_mutex);
- ret=read_some(command_desc,buf,BUFSIZE);
- pthread_mutex_unlock(&command_mutex);
- if(ret>0)
- {
- buf[ret]=0;
- printf("%s",buf);
- }
- }
- return 0;
- }
- void finalize()
- {
- close(command_desc);
- close(speech_desc);
- free(winbuf);
- }
- void* readStdin(void* ptr)
- {
- while(1)/*read enviornment*/
- {
- fgets(buf,BUFSIZE,stdin);
- #ifdef SEND_ENVIORNMENT
- pthread_mutex_lock(&command_mutex);
- write_buf(command_desc,buf,strlen(buf));
- pthread_mutex_unlock(&command_mutex);
- #endif
- if(feof(stdin) || buf[0]=='\n')
- {
- break;
- }
- }
- while(connected)
- {
- fgets(buf,BUFSIZE,stdin);
- pthread_mutex_lock(&command_mutex);
- write_buf(command_desc,buf,strlen(buf));
- pthread_mutex_unlock(&command_mutex);
- }
- pthread_exit(NULL);
- }
- void* readSignal(void* ptr)
- {
- while(connected)
- {
- read_full(3,window,WINSIZE);
- write_buf(speech_desc,window,WINSIZE);
- }
- pthread_exit(NULL);
- }
- void read_full(int file, char* buffer, int num)
- {
- int count,pos=0;
- while(num)
- {
- count=read(file,buffer+pos,num);
- if(count==0 || (count<0 && errno!=EAGAIN))
- {
- connected=0;
- return;
- }
- num-=count;
- pos+=count;
- }
- }
- int connect_to_host(char* name, int port)
- {
- int address;
- struct hostent* host_entity;
- int res,desc;
- int opts;
- struct sockaddr_in host;
- /* get address */
- if(!strcmp(name,"localhost"))
- address=htonl(2130706433); /*127.0.0.1*/
- else
- {
- address=inet_addr(name); /* check if it's an IP that's written in the string */
- if(address==(in_addr_t)-1)
- {
- host_entity = gethostbyname(name); /* search for the host under this name */
- if(!host_entity)
- {
- fprintf(stderr,"EAGI proxy: Wrong address!\n"); /* can't find anything*/
- return -1;
- }
- address=*((int*)host_entity->h_addr);
- }
- }
- desc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
- if(desc<0)
- {
- fprintf(stderr,"EAGI proxy: Cannot create socket!\n");
- return -1;
- }
- memset((void*)&host,0,sizeof(struct sockaddr_in));
- host.sin_family=AF_INET;
- host.sin_port=htons(port);
- host.sin_addr.s_addr=address;
- res=connect(desc,(struct sockaddr*)&host,sizeof(host));
- if(res<0)
- {
- fprintf(stderr,"EAGI proxy: Cannot connect!\n");
- return -1;
- }
- /* set to non-blocking mode */
- opts = fcntl(desc,F_GETFL);
- if (opts < 0) {
- perror("fcntl(F_GETFL)");
- exit(EXIT_FAILURE);
- }
- opts = (opts | O_NONBLOCK);
- if (fcntl(desc,F_SETFL,opts) < 0) {
- perror("fcntl(F_SETFL)");
- exit(EXIT_FAILURE);
- }
- return desc;
- }
- int read_some(int desc, char* buffer, int size)
- {
- unsigned char c;
- int res,i=0;
- for(;;)
- {
- res=read(desc,&c,1);
- if(res<1)
- {
- if(errno!=EAGAIN)
- {
- perror("Error reading");
- connected=0;
- }
- break;
- }
- if(res==0)
- {
- connected=0;
- break;
- }
- buffer[i]=c;
- i++;
- }
- return i;
- }
- /* This is a tricky function! */
- void write_buf(int desc, char* buf, int size)
- {
- int ret;
- /*NOTE: AMAP -> as much as possible */
- if(be!=bs)/* if data left in buffer */
- {
- if(be>bs)/* if buffer not split */
- {
- ret=write_amap(desc,bs,be-bs);/* write AMAP */
- bs+=ret;/* shift the start of the buffer */
- }
- else/* if buffer is split */
- {
- ret=write_amap(desc,bs,end-bs);/* write higher part first */
- if(ret==end-bs)/* if wrote whole of the higher part */
- {
- ret=write_amap(desc,winbuf,be-winbuf);/* write lower part */
- bs=winbuf+ret;/* shift start to new position */
- }
- else bs+=ret;/* if not wrote whole of higher part, only shift start */
- }
- }
- if(be==bs)/* if buffer is empty now */
- {
- ret=write_amap(desc,buf,size);/* write AMAP of the new data */
- buf+=ret;/* shift start of new data */
- size-=ret;/* lower size of new data */
- }
- if(size)/* if new data still remains unsent */
- {
- if(be>=bs)/* if data not split */
- {
- if(size>end-be)/* if new data size doesn't fit higher end */
- {
- size-=end-be;/* reduce new data size by the higher end size */
- memcpy(be,buf,end-be);/* copy to higher end */
- be=winbuf;/* shift end to begining of buffer */
- buf+=end-be;/* shift start of new data */
- }
- else/* if new data fits the higher end */
- {
- memcpy(be,buf,size);/* copy to higher end */
- be+=size;/* shift end by size */
- if(be>=end)/* if end goes beyond the buffer */
- be=winbuf;/* restart */
- size=0;/* everything copied */
- }
- }
- if(size)/* if new data still remains */
- {
- if(size>=bs-be)/* if new data doesn't fit between end and start */
- {
- fprintf(stderr,"Buffer overflow!\n");
- size=bs-be-1;/* reduce the size that we can copy */
- }
- if(size)/* if we can copy anything */
- {
- memcpy(be,buf,size);/* copy the new data between end and start */
- be+=size;/* shift end by size */
- }
- }
- }
- }
- int write_amap(int desc, char* buf, int size)
- {
- int ret;
- ret=write(desc,buf,size);
- if(ret<0)
- {
- if(errno!=EAGAIN)
- {
- perror("Error writing");
- connected=0;
- }
- return 0;
- }
- if(ret==0)
- connected=0;
- return ret;
- }
- void setnonblocking(int desc)
- {
- int opts;
- opts = fcntl(desc,F_GETFL);
- if(opts < 0)
- {
- perror("fcntl(F_GETFL)");
- exit(-1);
- }
- opts = (opts | O_NONBLOCK );
- if(fcntl(desc,F_SETFL,opts) < 0)
- {
- perror("fcntl(F_SETFL)");
- exit(-1);
- }
- }
|