Asterisk - The Open Source Telephony Project  18.5.0
eagi_proxy.c
Go to the documentation of this file.
1 /*
2  * Asterisk EAGI -> TCP/IP proxy
3  * by Danijel Korzinek (devil_slayer _at_ hotmail.com)
4  *
5  * This simple C application allows you to control asterisk thru one TCP/IP
6  * socket and listen to the conversation thru another socket.
7  *
8  * Great for ASR or wizzard-of-oz telephony systems!
9  *
10  * HOWTO:
11  * The program is compiled using the following command:
12  * gcc eagi_proxy.c -o eagi_proxy -lpthread
13  *
14  * In the dialplan, you can add something like this to the main context:
15  * exten => s,1,Answer
16  * exten => s,n,EAGI(/path/to/eagi_proxy)
17  * exten => s,n,Hangup
18  *
19  * To test the program you can use the netcat utility:
20  * (http://netcat.sourceforge.net/)
21  *
22  * -in one console run:
23  * nc -vv -l -p 8418 > /path/to/file.raw
24  * -in another run:
25  * nc -vv -l -p 8417
26  * -you can use any file for the signal or even /dev/null
27  * -you can change the port numbers in the sourcecode below
28  *
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),
33  * e.g.:
34  * GET DATA /path/to/gsm/file 10000 4
35  *
36  * Finally, you can open the RAW file in any sound editor. The format is:
37  * RAW little-endian 8kHz 16bit
38  */
39 
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <stdio.h>
43 #include <netinet/in.h>
44 #include <netinet/tcp.h>
45 #include <string.h>
46 #include <time.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <sys/socket.h>
50 #include <sys/time.h>
51 #include <arpa/inet.h>
52 #include <netdb.h>
53 #include <fcntl.h>
54 #include <errno.h>
55 #include <ctype.h>
56 #include <pthread.h>
57 
58 /* DEFINES */
59 #define SIGNAL_PORT 8418
60 #define COMMAND_PORT 8417
61 #define SEND_ENVIORNMENT /*send the enviornment thru the socket*/
62 /************************/
63 
64 
65 #define BUFSIZE 1024
66 char buf[BUFSIZE];
67 
68 #define WINSIZE 400 /* 25 ms @ 8 kHz and 16bit */
70 
71 #define WINBUF_NUM 2400 /* number of WINSIZE windows = 1 minute */
72 char* winbuf;
73 char *end, *bs, *be;
74 /* winbuf - start of buffer
75  * end - end of buffer
76  * bs - start of data
77  * be - end of data
78  */
79 
80 int command_desc; /* command transfer descriptor */
81 int speech_desc; /* speech signal descrriptor */
82 char connected=1; /* connection state */
83 
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*/
85 
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 */
88 
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*/
91 
92 void setnonblocking(int desc); /*sets the socket non-blocking; for polling */
93 
94 void finalize(); /* this function is run at exit */
95 
96 pthread_mutex_t command_mutex;/* command socket mutex */
98 void* readStdin(void* ptr);
99 void* readSignal(void* ptr);
100 
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
105  */
106 
107 int main()
108 {
109  int ret;
110 
111  atexit(finalize);
112 
113  setlinebuf(stdin);
114  setlinebuf(stdout);
115 
118  bs=be=winbuf;
119 
121  if(speech_desc<0)
122  {
123  perror("signal socket");
124  return -1;
125  }
126 
127 
129  if(command_desc<0)
130  {
131  perror("command socket");
132  return -1;
133  }
134 
138 
139  while(connected)
140  {
144  if(ret>0)
145  {
146  buf[ret]=0;
147  printf("%s",buf);
148  }
149  }
150 
151  return 0;
152 }
153 
154 void finalize()
155 {
156  close(command_desc);
157  close(speech_desc);
158  free(winbuf);
159 }
160 
161 void* readStdin(void* ptr)
162 {
163  while(1)/*read enviornment*/
164  {
165  fgets(buf,BUFSIZE,stdin);
166  #ifdef SEND_ENVIORNMENT
168  write_buf(command_desc,buf,strlen(buf));
170  #endif
171  if(feof(stdin) || buf[0]=='\n')
172  {
173  break;
174  }
175  }
176 
177  while(connected)
178  {
179  fgets(buf,BUFSIZE,stdin);
181  write_buf(command_desc,buf,strlen(buf));
183  }
184 
185  pthread_exit(NULL);
186 }
187 
188 void* readSignal(void* ptr)
189 {
190  while(connected)
191  {
194  }
195 
196  pthread_exit(NULL);
197 }
198 
199 
200 void read_full(int file, char* buffer, int num)
201 {
202  int count,pos=0;
203 
204  while(num)
205  {
206  count=read(file,buffer+pos,num);
207  if(count==0 || (count<0 && errno!=EAGAIN))
208  {
209  connected=0;
210  return;
211  }
212  num-=count;
213  pos+=count;
214  }
215 }
216 
217 int connect_to_host(char* name, int port)
218 {
219  int address;
220  struct hostent* host_entity;
221  int res,desc;
222  int opts;
223  struct sockaddr_in host;
224 
225 
226  /* get address */
227  if(!strcmp(name,"localhost"))
228  address=htonl(2130706433); /*127.0.0.1*/
229  else
230  {
231  address=inet_addr(name); /* check if it's an IP that's written in the string */
232  if(address==(in_addr_t)-1)
233  {
234  host_entity = gethostbyname(name); /* search for the host under this name */
235 
236  if(!host_entity)
237  {
238  fprintf(stderr,"EAGI proxy: Wrong address!\n"); /* can't find anything*/
239  return -1;
240  }
241  address=*((int*)host_entity->h_addr);
242  }
243  }
244 
245  desc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
246  if(desc<0)
247  {
248  fprintf(stderr,"EAGI proxy: Cannot create socket!\n");
249  return -1;
250  }
251 
252  memset((void*)&host,0,sizeof(struct sockaddr_in));
253 
254  host.sin_family=AF_INET;
255  host.sin_port=htons(port);
256  host.sin_addr.s_addr=address;
257 
258  res=connect(desc,(struct sockaddr*)&host,sizeof(host));
259  if(res<0)
260  {
261  fprintf(stderr,"EAGI proxy: Cannot connect!\n");
262  return -1;
263  }
264 
265  /* set to non-blocking mode */
266  opts = fcntl(desc,F_GETFL);
267  if (opts < 0) {
268  perror("fcntl(F_GETFL)");
269  exit(EXIT_FAILURE);
270  }
271  opts = (opts | O_NONBLOCK);
272  if (fcntl(desc,F_SETFL,opts) < 0) {
273  perror("fcntl(F_SETFL)");
274  exit(EXIT_FAILURE);
275  }
276 
277 
278  return desc;
279 }
280 
281 int read_some(int desc, char* buffer, int size)
282 {
283  unsigned char c;
284  int res,i=0;
285 
286  for(;;)
287  {
288  res=read(desc,&c,1);
289  if(res<1)
290  {
291  if(errno!=EAGAIN)
292  {
293  perror("Error reading");
294  connected=0;
295  }
296  break;
297  }
298  if(res==0)
299  {
300  connected=0;
301  break;
302  }
303 
304  buffer[i]=c;
305  i++;
306  }
307 
308  return i;
309 }
310 
311 /* This is a tricky function! */
312 void write_buf(int desc, char* buf, int size)
313 {
314  int ret;
315 
316  /*NOTE: AMAP -> as much as possible */
317 
318  if(be!=bs)/* if data left in buffer */
319  {
320  if(be>bs)/* if buffer not split */
321  {
322  ret=write_amap(desc,bs,be-bs);/* write AMAP */
323  bs+=ret;/* shift the start of the buffer */
324  }
325  else/* if buffer is split */
326  {
327  ret=write_amap(desc,bs,end-bs);/* write higher part first */
328  if(ret==end-bs)/* if wrote whole of the higher part */
329  {
330  ret=write_amap(desc,winbuf,be-winbuf);/* write lower part */
331  bs=winbuf+ret;/* shift start to new position */
332  }
333  else bs+=ret;/* if not wrote whole of higher part, only shift start */
334  }
335  }
336 
337  if(be==bs)/* if buffer is empty now */
338  {
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 */
342  }
343 
344  if(size)/* if new data still remains unsent */
345  {
346  if(be>=bs)/* if data not split */
347  {
348  if(size>end-be)/* if new data size doesn't fit higher end */
349  {
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 */
354  }
355  else/* if new data fits the higher end */
356  {
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 */
362  }
363  }
364 
365  if(size)/* if new data still remains */
366  {
367  if(size>=bs-be)/* if new data doesn't fit between end and start */
368  {
369  fprintf(stderr,"Buffer overflow!\n");
370  size=bs-be-1;/* reduce the size that we can copy */
371  }
372 
373  if(size)/* if we can copy anything */
374  {
375  memcpy(be,buf,size);/* copy the new data between end and start */
376  be+=size;/* shift end by size */
377  }
378  }
379  }
380 }
381 
382 int write_amap(int desc, char* buf, int size)
383 {
384  int ret;
385  ret=write(desc,buf,size);
386  if(ret<0)
387  {
388  if(errno!=EAGAIN)
389  {
390  perror("Error writing");
391  connected=0;
392  }
393  return 0;
394  }
395  if(ret==0)
396  connected=0;
397 
398  return ret;
399 }
400 
401 
403 {
404  int opts;
405 
406  opts = fcntl(desc,F_GETFL);
407  if(opts < 0)
408  {
409  perror("fcntl(F_GETFL)");
410  exit(-1);
411  }
412 
413  opts = (opts | O_NONBLOCK );
414  if(fcntl(desc,F_SETFL,opts) < 0)
415  {
416  perror("fcntl(F_SETFL)");
417  exit(-1);
418  }
419 }
#define pthread_mutex_init
Definition: lock.h:626
int read_some(int file, char *buffer, int size)
Definition: eagi_proxy.c:281
#define gethostbyname
Definition: lock.h:637
#define EXIT_FAILURE
Time-related functions and macros.
char * address
Definition: f2c.h:59
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
int main()
Definition: eagi_proxy.c:107
static const char desc[]
Definition: cdr_mysql.c:73
char * be
Definition: eagi_proxy.c:73
static struct test_val c
#define NULL
Definition: resample.c:96
char * end
Definition: eagi_proxy.c:73
pthread_t signal_thread
Definition: eagi_proxy.c:97
#define pthread_mutex_t
Definition: lock.h:620
pthread_mutex_t command_mutex
Definition: eagi_proxy.c:96
char * bs
Definition: eagi_proxy.c:73
int speech_desc
Definition: eagi_proxy.c:81
char * malloc()
void write_buf(int file, char *buffer, int num)
Definition: eagi_proxy.c:312
void free()
static char host[256]
Definition: muted.c:77
#define WINBUF_NUM
Definition: eagi_proxy.c:71
#define pthread_mutex_lock
Definition: lock.h:623
#define BUFSIZE
Definition: eagi_proxy.c:65
#define SIGNAL_PORT
Definition: eagi_proxy.c:59
void * readSignal(void *ptr)
Definition: eagi_proxy.c:188
void * readStdin(void *ptr)
Definition: eagi_proxy.c:161
pthread_t stdin_thread
Definition: eagi_proxy.c:97
int errno
int write_amap(int file, char *buffer, int num)
Definition: eagi_proxy.c:382
static const char name[]
Definition: cdr_mysql.c:74
#define pthread_create
Definition: lock.h:640
int connect_to_host(char *host, int port)
Definition: eagi_proxy.c:217
void setnonblocking(int desc)
Definition: eagi_proxy.c:402
void read_full(int file, char *buffer, int num)
Definition: eagi_proxy.c:200
char window[WINSIZE]
Definition: eagi_proxy.c:69
#define COMMAND_PORT
Definition: eagi_proxy.c:60
int command_desc
Definition: eagi_proxy.c:80
void finalize()
Definition: eagi_proxy.c:154
#define WINSIZE
Definition: eagi_proxy.c:68
#define pthread_mutex_unlock
Definition: lock.h:624
char * winbuf
Definition: eagi_proxy.c:72
char connected
Definition: eagi_proxy.c:82