feat: AudioSocket channel, application, and ARI support.
[asterisk/asterisk.git] / res / res_audiosocket.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2019, CyCore Systems, Inc
5  *
6  * Seán C McCord <scm@cycoresys.com
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief AudioSocket support for Asterisk
22  *
23  * \author Seán C McCord <scm@cycoresys.com>
24  *
25  */
26
27 /*** MODULEINFO
28         <support_level>extended</support_level>
29  ***/
30
31 #include "asterisk.h"
32 #include "errno.h"
33 #include <uuid/uuid.h>
34
35 #include "asterisk/file.h"
36 #include "asterisk/res_audiosocket.h"
37 #include "asterisk/channel.h"
38 #include "asterisk/module.h"
39 #include "asterisk/uuid.h"
40 #include "asterisk/format_cache.h"
41
42 #define MODULE_DESCRIPTION      "AudioSocket support functions for Asterisk"
43
44 #define MAX_CONNECT_TIMEOUT_MSEC 2000
45
46 /*!
47  * \internal
48  * \brief Attempt to complete the audiosocket connection.
49  *
50  * \param server Url that we are trying to connect to.
51  * \param addr Address that host was resolved to.
52  * \param netsockfd File descriptor of socket.
53  *
54  * \retval 0 when connection is succesful.
55  * \retval 1 when there is an error.
56  */
57 static int handle_audiosocket_connection(const char *server,
58         const struct ast_sockaddr addr, const int netsockfd)
59 {
60         struct pollfd pfds[1];
61         int res, conresult;
62         socklen_t reslen;
63
64         reslen = sizeof(conresult);
65
66         pfds[0].fd = netsockfd;
67         pfds[0].events = POLLOUT;
68
69         while ((res = ast_poll(pfds, 1, MAX_CONNECT_TIMEOUT_MSEC)) != 1) {
70                 if (errno != EINTR) {
71                         if (!res) {
72                                 ast_log(LOG_WARNING, "AudioSocket connection to '%s' timed"
73                                         "out after MAX_CONNECT_TIMEOUT_MSEC (%d) milliseconds.\n",
74                                         server, MAX_CONNECT_TIMEOUT_MSEC);
75                         } else {
76                                 ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", server,
77                                         strerror(errno));
78                         }
79
80                         return -1;
81                 }
82         }
83
84         if (getsockopt(pfds[0].fd, SOL_SOCKET, SO_ERROR, &conresult, &reslen) < 0) {
85                 ast_log(LOG_WARNING, "Connection to %s failed with error: %s\n",
86                         ast_sockaddr_stringify(&addr), strerror(errno));
87                 return -1;
88         }
89
90         if (conresult) {
91                 ast_log(LOG_WARNING, "Connecting to '%s' failed for url '%s': %s\n",
92                         ast_sockaddr_stringify(&addr), server, strerror(conresult));
93                 return -1;
94         }
95
96         return 0;
97 }
98
99 const int ast_audiosocket_connect(const char *server, struct ast_channel *chan)
100 {
101         int s = -1;
102         struct ast_sockaddr *addrs;
103         int num_addrs = 0, i = 0;
104
105         if (chan && ast_autoservice_start(chan) < 0) {
106                 ast_log(LOG_WARNING, "Failed to start autoservice for channel "
107                         "%s\n", ast_channel_name(chan));
108                 goto end;
109         }
110
111         if (ast_strlen_zero(server)) {
112                 ast_log(LOG_ERROR, "No AudioSocket server provided\n");
113                 goto end;
114         }
115
116         if (!(num_addrs = ast_sockaddr_resolve(&addrs, server, PARSE_PORT_REQUIRE,
117                 AST_AF_UNSPEC))) {
118                 ast_log(LOG_ERROR, "Failed to resolve AudioSocket service using %s - "
119                         "requires a valid hostname and port\n", server);
120                 goto end;
121         }
122
123         /* Connect to AudioSocket service */
124         for (i = 0; i < num_addrs; i++) {
125
126                 if (!ast_sockaddr_port(&addrs[i])) {
127                         /* If there's no port, other addresses should have the
128                          * same problem. Stop here.
129                          */
130                         ast_log(LOG_ERROR, "No port provided for %s\n",
131                                 ast_sockaddr_stringify(&addrs[i]));
132                         s = -1;
133                         goto end;
134                 }
135
136                 if ((s = ast_socket_nonblock(addrs[i].ss.ss_family, SOCK_STREAM,
137                         IPPROTO_TCP)) < 0) {
138                         ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
139                         continue;
140                 }
141
142                 if (ast_connect(s, &addrs[i]) && errno == EINPROGRESS) {
143
144                         if (handle_audiosocket_connection(server, addrs[i], s)) {
145                                 close(s);
146                                 continue;
147                         }
148
149                 } else {
150                         ast_log(LOG_ERROR, "Connection to %s failed with unexpected error: %s\n",
151                                 ast_sockaddr_stringify(&addrs[i]), strerror(errno));
152                         close(s);
153                         s = -1;
154                 }
155
156                 break;
157         }
158
159 end:
160         if (addrs) {
161                 ast_free(addrs);
162         }
163
164         if (chan && ast_autoservice_stop(chan) < 0) {
165                 ast_log(LOG_WARNING, "Failed to stop autoservice for channel %s\n",
166                 ast_channel_name(chan));
167                 close(s);
168                 return -1;
169         }
170
171         if (i == num_addrs) {
172                 ast_log(LOG_ERROR, "Failed to connect to AudioSocket service\n");
173                 close(s);
174                 return -1;
175         }
176
177         return s;
178 }
179
180 const int ast_audiosocket_init(const int svc, const char *id)
181 {
182         uuid_t uu;
183         int ret = 0;
184         uint8_t buf[3 + 16];
185
186         if (ast_strlen_zero(id)) {
187                 ast_log(LOG_ERROR, "No UUID for AudioSocket\n");
188                 return -1;
189         }
190
191         if (uuid_parse(id, uu)) {
192                 ast_log(LOG_ERROR, "Failed to parse UUID '%s'\n", id);
193                 return -1;
194         }
195
196         buf[0] = 0x01;
197         buf[1] = 0x00;
198         buf[2] = 0x10;
199         memcpy(buf + 3, uu, 16);
200
201         if (write(svc, buf, 3 + 16) != 3 + 16) {
202                 ast_log(LOG_WARNING, "Failed to write data to AudioSocket\n");
203                 ret = -1;
204         }
205
206         return ret;
207 }
208
209 const int ast_audiosocket_send_frame(const int svc, const struct ast_frame *f)
210 {
211         int ret = 0;
212         uint8_t kind = 0x10;    /* always 16-bit, 8kHz signed linear mono, for now */
213         uint8_t *p;
214         uint8_t buf[3 + f->datalen];
215
216         p = buf;
217
218         *(p++) = kind;
219         *(p++) = f->datalen >> 8;
220         *(p++) = f->datalen & 0xff;
221         memcpy(p, f->data.ptr, f->datalen);
222
223         if (write(svc, buf, 3 + f->datalen) != 3 + f->datalen) {
224                 ast_log(LOG_WARNING, "Failed to write data to AudioSocket\n");
225                 ret = -1;
226         }
227
228         return ret;
229 }
230
231 struct ast_frame *ast_audiosocket_receive_frame(const int svc)
232 {
233
234         int i = 0, n = 0, ret = 0, not_audio = 0;
235         struct ast_frame f = {
236                 .frametype = AST_FRAME_VOICE,
237                 .subclass.format = ast_format_slin,
238                 .src = "AudioSocket",
239                 .mallocd = AST_MALLOCD_DATA,
240         };
241         uint8_t kind;
242         uint8_t len_high;
243         uint8_t len_low;
244         uint16_t len = 0;
245         uint8_t *data;
246
247         n = read(svc, &kind, 1);
248         if (n < 0 && errno == EAGAIN) {
249                 return &ast_null_frame;
250         }
251         if (n == 0) {
252                 return &ast_null_frame;
253         }
254         if (n != 1) {
255                 ast_log(LOG_WARNING, "Failed to read type header from AudioSocket\n");
256                 return NULL;
257         }
258         if (kind == 0x00) {
259                 /* AudioSocket ended by remote */
260                 return NULL;
261         }
262         if (kind != 0x10) {
263                 /* read but ignore non-audio message */
264                 ast_log(LOG_WARNING, "Received non-audio AudioSocket message\n");
265                 not_audio = 1;
266         }
267
268         n = read(svc, &len_high, 1);
269         if (n != 1) {
270                 ast_log(LOG_WARNING, "Failed to read data length from AudioSocket\n");
271                 return NULL;
272         }
273         len += len_high * 256;
274         n = read(svc, &len_low, 1);
275         if (n != 1) {
276                 ast_log(LOG_WARNING, "Failed to read data length from AudioSocket\n");
277                 return NULL;
278         }
279         len += len_low;
280
281         if (len < 1) {
282                 return &ast_null_frame;
283         }
284
285         data = ast_malloc(len);
286         if (!data) {
287                 ast_log(LOG_ERROR, "Failed to allocate for data from AudioSocket\n");
288                 return NULL;
289         }
290
291         ret = 0;
292         n = 0;
293         i = 0;
294         while (i < len) {
295                 n = read(svc, data + i, len - i);
296                 if (n < 0) {
297                         ast_log(LOG_ERROR, "Failed to read data from AudioSocket\n");
298                         ret = n;
299                         break;
300                 }
301                 if (n == 0) {
302                         ast_log(LOG_ERROR, "Insufficient data read from AudioSocket\n");
303                         ret = -1;
304                         break;
305                 }
306                 i += n;
307         }
308
309         if (ret != 0) {
310                 ast_free(data);
311                 return NULL;
312         }
313
314         if (not_audio) {
315                 ast_free(data);
316                 return &ast_null_frame;
317         }
318
319         f.data.ptr = data;
320         f.datalen = len;
321         f.samples = len / 2;
322
323         /* The frame steals data, so it doesn't need to be freed here */
324         return ast_frisolate(&f);
325 }
326
327 static int load_module(void)
328 {
329         ast_verb(1, "Loading AudioSocket Support module\n");
330         return AST_MODULE_LOAD_SUCCESS;
331 }
332
333 static int unload_module(void)
334 {
335         ast_verb(1, "Unloading AudioSocket Support module\n");
336         return AST_MODULE_LOAD_SUCCESS;
337 }
338
339 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER,
340         "AudioSocket support",
341         .support_level = AST_MODULE_SUPPORT_EXTENDED,
342         .load = load_module,
343         .unload = unload_module,
344         .load_pri = AST_MODPRI_CHANNEL_DEPEND,
345 );