2 * Asterisk EAGI -> TCP/IP proxy
3 * by Danijel Korzinek (devil_slayer _at_ hotmail.com)
5 * This simple C application allows you to control asterisk thru one TCP/IP
6 * socket and listen to the conversation thru another socket.
8 * Great for ASR or wizzard-of-oz telephony systems!
11 * The program is compiled using the following command:
12 * gcc eagi_proxy.c -o eagi_proxy -lpthread
14 * In the dialplan, you can add something like this to the main context:
16 * exten => s,n,EAGI(/path/to/eagi_proxy)
19 * To test the program you can use the netcat utility:
20 * (http://netcat.sourceforge.net/)
22 * -in one console run:
23 * nc -vv -l -p 8418 > /path/to/file.raw
26 * -you can use any file for the signal or even /dev/null
27 * -you can change the port numbers in the sourcecode below
29 * Once you make the call, both programs will accept the incoming
30 * connection. The program on port 8417 will print out the enviornemnt
31 * (unless the appropriate define below is commented) and you can write
32 * any AGI command there (http://www.voip-info.org/wiki-Asterisk+AGI),
34 * GET DATA /path/to/gsm/file 10000 4
36 * Finally, you can open the RAW file in any sound editor. The format is:
37 * RAW little-endian 8kHz 16bit
43 #include <netinet/in.h>
44 #include <netinet/tcp.h>
47 #include <sys/types.h>
49 #include <sys/socket.h>
51 #include <arpa/inet.h>
59 #define SIGNAL_PORT 8418
60 #define COMMAND_PORT 8417
61 #define SEND_ENVIORNMENT /*send the enviornment thru the socket*/
62 /************************/
68 #define WINSIZE 400 /* 25 ms @ 8 kHz and 16bit */
71 #define WINBUF_NUM 2400 /* number of WINSIZE windows = 1 minute */
74 /* winbuf - start of buffer
80 int command_desc; /* command transfer descriptor */
81 int speech_desc; /* speech signal descrriptor */
82 char connected=1; /* connection state */
84 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*/
86 void read_full(int file, char* buffer, int num); /* read EXACTLY "num" ammount of bytes from "file" descriptor to "buffer"*/
87 int read_some(int file, char* buffer, int size); /* read AT MOST "size" ammount of bytes */
89 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 */
90 int write_amap(int file, char* buffer, int num); /*write AT MOST "num" ammount of bytes and return ammount that was written*/
92 void setnonblocking(int desc); /*sets the socket non-blocking; for polling */
94 void finalize(); /* this function is run at exit */
96 pthread_mutex_t command_mutex;/* command socket mutex */
97 pthread_t stdin_thread,signal_thread;
98 void* readStdin(void* ptr);
99 void* readSignal(void* ptr);
101 /* The program creates 3 threads:
102 * 1) Main thread - reads commands from the socket and sends them to asterisk
103 * 2) stdin_thread - reads asterisk output and sends it to the command socket
104 * 3) signal_thread - reads the sound from asterisk and sends it to the signal socket
116 winbuf=(char*)malloc(WINSIZE*WINBUF_NUM);
117 end=winbuf+WINSIZE*WINBUF_NUM;
120 speech_desc=connect_to_host("localhost",SIGNAL_PORT);
123 perror("signal socket");
128 command_desc=connect_to_host("localhost",COMMAND_PORT);
131 perror("command socket");
135 pthread_mutex_init(&command_mutex,NULL);
136 pthread_create(&stdin_thread,NULL,readStdin,NULL);
137 pthread_create(&signal_thread,NULL,readSignal,NULL);
141 pthread_mutex_lock(&command_mutex);
142 ret=read_some(command_desc,buf,BUFSIZE);
143 pthread_mutex_unlock(&command_mutex);
161 void* readStdin(void* ptr)
163 while(1)/*read enviornment*/
165 fgets(buf,BUFSIZE,stdin);
166 #ifdef SEND_ENVIORNMENT
167 pthread_mutex_lock(&command_mutex);
168 write_buf(command_desc,buf,strlen(buf));
169 pthread_mutex_unlock(&command_mutex);
171 if(feof(stdin) || buf[0]=='\n')
179 fgets(buf,BUFSIZE,stdin);
180 pthread_mutex_lock(&command_mutex);
181 write_buf(command_desc,buf,strlen(buf));
182 pthread_mutex_unlock(&command_mutex);
188 void* readSignal(void* ptr)
192 read_full(3,window,WINSIZE);
193 write_buf(speech_desc,window,WINSIZE);
200 void read_full(int file, char* buffer, int num)
206 count=read(file,buffer+pos,num);
207 if(count==0 || (count<0 && errno!=EAGAIN))
217 int connect_to_host(char* name, int port)
220 struct hostent* host_entity;
223 struct sockaddr_in host;
227 if(!strcmp(name,"localhost"))
228 address=htonl(2130706433); /*127.0.0.1*/
231 address=inet_addr(name); /* check if it's an IP that's written in the string */
232 if(address==(in_addr_t)-1)
234 host_entity = gethostbyname(name); /* search for the host under this name */
238 fprintf(stderr,"EAGI proxy: Wrong address!\n"); /* can't find anything*/
241 address=*((int*)host_entity->h_addr);
245 desc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
248 fprintf(stderr,"EAGI proxy: Cannot create socket!\n");
252 memset((void*)&host,0,sizeof(struct sockaddr_in));
254 host.sin_family=AF_INET;
255 host.sin_port=htons(port);
256 host.sin_addr.s_addr=address;
258 res=connect(desc,(struct sockaddr*)&host,sizeof(host));
261 fprintf(stderr,"EAGI proxy: Cannot connect!\n");
265 /* set to non-blocking mode */
266 opts = fcntl(desc,F_GETFL);
268 perror("fcntl(F_GETFL)");
271 opts = (opts | O_NONBLOCK);
272 if (fcntl(desc,F_SETFL,opts) < 0) {
273 perror("fcntl(F_SETFL)");
281 int read_some(int desc, char* buffer, int size)
293 perror("Error reading");
311 /* This is a tricky function! */
312 void write_buf(int desc, char* buf, int size)
316 /*NOTE: AMAP -> as much as possible */
318 if(be!=bs)/* if data left in buffer */
320 if(be>bs)/* if buffer not split */
322 ret=write_amap(desc,bs,be-bs);/* write AMAP */
323 bs+=ret;/* shift the start of the buffer */
325 else/* if buffer is split */
327 ret=write_amap(desc,bs,end-bs);/* write higher part first */
328 if(ret==end-bs)/* if wrote whole of the higher part */
330 ret=write_amap(desc,winbuf,be-winbuf);/* write lower part */
331 bs=winbuf+ret;/* shift start to new position */
333 else bs+=ret;/* if not wrote whole of higher part, only shift start */
337 if(be==bs)/* if buffer is empty now */
339 ret=write_amap(desc,buf,size);/* write AMAP of the new data */
340 buf+=ret;/* shift start of new data */
341 size-=ret;/* lower size of new data */
344 if(size)/* if new data still remains unsent */
346 if(be>=bs)/* if data not split */
348 if(size>end-be)/* if new data size doesn't fit higher end */
350 size-=end-be;/* reduce new data size by the higher end size */
351 memcpy(be,buf,end-be);/* copy to higher end */
352 be=winbuf;/* shift end to begining of buffer */
353 buf+=end-be;/* shift start of new data */
355 else/* if new data fits the higher end */
357 memcpy(be,buf,size);/* copy to higher end */
358 be+=size;/* shift end by size */
359 if(be>=end)/* if end goes beyond the buffer */
360 be=winbuf;/* restart */
361 size=0;/* everything copied */
365 if(size)/* if new data still remains */
367 if(size>=bs-be)/* if new data doesn't fit between end and start */
369 fprintf(stderr,"Buffer overflow!\n");
370 size=bs-be-1;/* reduce the size that we can copy */
373 if(size)/* if we can copy anything */
375 memcpy(be,buf,size);/* copy the new data between end and start */
376 be+=size;/* shift end by size */
382 int write_amap(int desc, char* buf, int size)
385 ret=write(desc,buf,size);
390 perror("Error writing");
402 void setnonblocking(int desc)
406 opts = fcntl(desc,F_GETFL);
409 perror("fcntl(F_GETFL)");
413 opts = (opts | O_NONBLOCK );
414 if(fcntl(desc,F_SETFL,opts) < 0)
416 perror("fcntl(F_SETFL)");