420 lines
9.1 KiB
C
420 lines
9.1 KiB
C
|
/*
|
||
|
* 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);
|
||
|
}
|
||
|
}
|