eagi_proxy.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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. #include <stdlib.h>
  40. #include <unistd.h>
  41. #include <stdio.h>
  42. #include <netinet/in.h>
  43. #include <netinet/tcp.h>
  44. #include <string.h>
  45. #include <time.h>
  46. #include <sys/types.h>
  47. #include <sys/stat.h>
  48. #include <sys/socket.h>
  49. #include <sys/time.h>
  50. #include <arpa/inet.h>
  51. #include <netdb.h>
  52. #include <fcntl.h>
  53. #include <errno.h>
  54. #include <ctype.h>
  55. #include <pthread.h>
  56. /* DEFINES */
  57. #define SIGNAL_PORT 8418
  58. #define COMMAND_PORT 8417
  59. #define SEND_ENVIORNMENT /*send the enviornment thru the socket*/
  60. /************************/
  61. #define BUFSIZE 1024
  62. char buf[BUFSIZE];
  63. #define WINSIZE 400 /* 25 ms @ 8 kHz and 16bit */
  64. char window[WINSIZE];
  65. #define WINBUF_NUM 2400 /* number of WINSIZE windows = 1 minute */
  66. char* winbuf;
  67. char *end, *bs, *be;
  68. /* winbuf - start of buffer
  69. * end - end of buffer
  70. * bs - start of data
  71. * be - end of data
  72. */
  73. int command_desc; /* command transfer descriptor */
  74. int speech_desc; /* speech signal descrriptor */
  75. char connected=1; /* connection state */
  76. 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*/
  77. void read_full(int file, char* buffer, int num); /* read EXACTLY "num" ammount of bytes from "file" descriptor to "buffer"*/
  78. int read_some(int file, char* buffer, int size); /* read AT MOST "size" ammount of bytes */
  79. 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 */
  80. int write_amap(int file, char* buffer, int num); /*write AT MOST "num" ammount of bytes and return ammount that was written*/
  81. void setnonblocking(int desc); /*sets the socket non-blocking; for polling */
  82. void finalize(); /* this function is run at exit */
  83. pthread_mutex_t command_mutex;/* command socket mutex */
  84. pthread_t stdin_thread,signal_thread;
  85. void* readStdin(void* ptr);
  86. void* readSignal(void* ptr);
  87. /* The program creates 3 threads:
  88. * 1) Main thread - reads commands from the socket and sends them to asterisk
  89. * 2) stdin_thread - reads asterisk output and sends it to the command socket
  90. * 3) signal_thread - reads the sound from asterisk and sends it to the signal socket
  91. */
  92. int main()
  93. {
  94. int ret;
  95. atexit(finalize);
  96. setlinebuf(stdin);
  97. setlinebuf(stdout);
  98. winbuf=(char*)malloc(WINSIZE*WINBUF_NUM);
  99. end=winbuf+WINSIZE*WINBUF_NUM;
  100. bs=be=winbuf;
  101. speech_desc=connect_to_host("localhost",SIGNAL_PORT);
  102. if(speech_desc<0)
  103. {
  104. perror("signal socket");
  105. return -1;
  106. }
  107. command_desc=connect_to_host("localhost",COMMAND_PORT);
  108. if(command_desc<0)
  109. {
  110. perror("command socket");
  111. return -1;
  112. }
  113. pthread_mutex_init(&command_mutex,NULL);
  114. pthread_create(&stdin_thread,NULL,readStdin,NULL);
  115. pthread_create(&signal_thread,NULL,readSignal,NULL);
  116. while(connected)
  117. {
  118. pthread_mutex_lock(&command_mutex);
  119. ret=read_some(command_desc,buf,BUFSIZE);
  120. pthread_mutex_unlock(&command_mutex);
  121. if(ret>0)
  122. {
  123. buf[ret]=0;
  124. printf("%s",buf);
  125. }
  126. }
  127. return 0;
  128. }
  129. void finalize()
  130. {
  131. close(command_desc);
  132. close(speech_desc);
  133. free(winbuf);
  134. }
  135. void* readStdin(void* ptr)
  136. {
  137. while(1)/*read enviornment*/
  138. {
  139. fgets(buf,BUFSIZE,stdin);
  140. #ifdef SEND_ENVIORNMENT
  141. pthread_mutex_lock(&command_mutex);
  142. write_buf(command_desc,buf,strlen(buf));
  143. pthread_mutex_unlock(&command_mutex);
  144. #endif
  145. if(feof(stdin) || buf[0]=='\n')
  146. {
  147. break;
  148. }
  149. }
  150. while(connected)
  151. {
  152. fgets(buf,BUFSIZE,stdin);
  153. pthread_mutex_lock(&command_mutex);
  154. write_buf(command_desc,buf,strlen(buf));
  155. pthread_mutex_unlock(&command_mutex);
  156. }
  157. pthread_exit(NULL);
  158. }
  159. void* readSignal(void* ptr)
  160. {
  161. while(connected)
  162. {
  163. read_full(3,window,WINSIZE);
  164. write_buf(speech_desc,window,WINSIZE);
  165. }
  166. pthread_exit(NULL);
  167. }
  168. void read_full(int file, char* buffer, int num)
  169. {
  170. int count,pos=0;
  171. while(num)
  172. {
  173. count=read(file,buffer+pos,num);
  174. if(count==0 || (count<0 && errno!=EAGAIN))
  175. {
  176. connected=0;
  177. return;
  178. }
  179. num-=count;
  180. pos+=count;
  181. }
  182. }
  183. int connect_to_host(char* name, int port)
  184. {
  185. int address;
  186. struct hostent* host_entity;
  187. int res,desc;
  188. int opts;
  189. struct sockaddr_in host;
  190. /* get address */
  191. if(!strcmp(name,"localhost"))
  192. address=htonl(2130706433); /*127.0.0.1*/
  193. else
  194. {
  195. address=inet_addr(name); /* check if it's an IP that's written in the string */
  196. if(address==(in_addr_t)-1)
  197. {
  198. host_entity = gethostbyname(name); /* search for the host under this name */
  199. if(!host_entity)
  200. {
  201. fprintf(stderr,"EAGI proxy: Wrong address!\n"); /* can't find anything*/
  202. return -1;
  203. }
  204. address=*((int*)host_entity->h_addr);
  205. }
  206. }
  207. desc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  208. if(desc<0)
  209. {
  210. fprintf(stderr,"EAGI proxy: Cannot create socket!\n");
  211. return -1;
  212. }
  213. memset((void*)&host,0,sizeof(struct sockaddr_in));
  214. host.sin_family=AF_INET;
  215. host.sin_port=htons(port);
  216. host.sin_addr.s_addr=address;
  217. res=connect(desc,(struct sockaddr*)&host,sizeof(host));
  218. if(res<0)
  219. {
  220. fprintf(stderr,"EAGI proxy: Cannot connect!\n");
  221. return -1;
  222. }
  223. /* set to non-blocking mode */
  224. opts = fcntl(desc,F_GETFL);
  225. if (opts < 0) {
  226. perror("fcntl(F_GETFL)");
  227. exit(EXIT_FAILURE);
  228. }
  229. opts = (opts | O_NONBLOCK);
  230. if (fcntl(desc,F_SETFL,opts) < 0) {
  231. perror("fcntl(F_SETFL)");
  232. exit(EXIT_FAILURE);
  233. }
  234. return desc;
  235. }
  236. int read_some(int desc, char* buffer, int size)
  237. {
  238. unsigned char c;
  239. int res,i=0;
  240. for(;;)
  241. {
  242. res=read(desc,&c,1);
  243. if(res<1)
  244. {
  245. if(errno!=EAGAIN)
  246. {
  247. perror("Error reading");
  248. connected=0;
  249. }
  250. break;
  251. }
  252. if(res==0)
  253. {
  254. connected=0;
  255. break;
  256. }
  257. buffer[i]=c;
  258. i++;
  259. }
  260. return i;
  261. }
  262. /* This is a tricky function! */
  263. void write_buf(int desc, char* buf, int size)
  264. {
  265. int ret;
  266. /*NOTE: AMAP -> as much as possible */
  267. if(be!=bs)/* if data left in buffer */
  268. {
  269. if(be>bs)/* if buffer not split */
  270. {
  271. ret=write_amap(desc,bs,be-bs);/* write AMAP */
  272. bs+=ret;/* shift the start of the buffer */
  273. }
  274. else/* if buffer is split */
  275. {
  276. ret=write_amap(desc,bs,end-bs);/* write higher part first */
  277. if(ret==end-bs)/* if wrote whole of the higher part */
  278. {
  279. ret=write_amap(desc,winbuf,be-winbuf);/* write lower part */
  280. bs=winbuf+ret;/* shift start to new position */
  281. }
  282. else bs+=ret;/* if not wrote whole of higher part, only shift start */
  283. }
  284. }
  285. if(be==bs)/* if buffer is empty now */
  286. {
  287. ret=write_amap(desc,buf,size);/* write AMAP of the new data */
  288. buf+=ret;/* shift start of new data */
  289. size-=ret;/* lower size of new data */
  290. }
  291. if(size)/* if new data still remains unsent */
  292. {
  293. if(be>=bs)/* if data not split */
  294. {
  295. if(size>end-be)/* if new data size doesn't fit higher end */
  296. {
  297. size-=end-be;/* reduce new data size by the higher end size */
  298. memcpy(be,buf,end-be);/* copy to higher end */
  299. be=winbuf;/* shift end to begining of buffer */
  300. buf+=end-be;/* shift start of new data */
  301. }
  302. else/* if new data fits the higher end */
  303. {
  304. memcpy(be,buf,size);/* copy to higher end */
  305. be+=size;/* shift end by size */
  306. if(be>=end)/* if end goes beyond the buffer */
  307. be=winbuf;/* restart */
  308. size=0;/* everything copied */
  309. }
  310. }
  311. if(size)/* if new data still remains */
  312. {
  313. if(size>=bs-be)/* if new data doesn't fit between end and start */
  314. {
  315. fprintf(stderr,"Buffer overflow!\n");
  316. size=bs-be-1;/* reduce the size that we can copy */
  317. }
  318. if(size)/* if we can copy anything */
  319. {
  320. memcpy(be,buf,size);/* copy the new data between end and start */
  321. be+=size;/* shift end by size */
  322. }
  323. }
  324. }
  325. }
  326. int write_amap(int desc, char* buf, int size)
  327. {
  328. int ret;
  329. ret=write(desc,buf,size);
  330. if(ret<0)
  331. {
  332. if(errno!=EAGAIN)
  333. {
  334. perror("Error writing");
  335. connected=0;
  336. }
  337. return 0;
  338. }
  339. if(ret==0)
  340. connected=0;
  341. return ret;
  342. }
  343. void setnonblocking(int desc)
  344. {
  345. int opts;
  346. opts = fcntl(desc,F_GETFL);
  347. if(opts < 0)
  348. {
  349. perror("fcntl(F_GETFL)");
  350. exit(-1);
  351. }
  352. opts = (opts | O_NONBLOCK );
  353. if(fcntl(desc,F_SETFL,opts) < 0)
  354. {
  355. perror("fcntl(F_SETFL)");
  356. exit(-1);
  357. }
  358. }