Asterisk - The Open Source Telephony Project  18.5.0
res_audiosocket.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2019, CyCore Systems, Inc
5  *
6  * Seán C McCord <[email protected]
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 <[email protected]>
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"
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 = NULL;
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 
232 {
233 
234  int i = 0, n = 0, ret = 0, not_audio = 0;
235  struct ast_frame f = {
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");
331 }
332 
333 static int unload_module(void)
334 {
335  ast_verb(1, "Unloading AudioSocket Support module\n");
337 }
338 
340  .support_level = AST_MODULE_SUPPORT_EXTENDED,
341  .load = load_module,
342  .unload = unload_module,
343  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
344 );
struct sockaddr_storage ss
Definition: netsock2.h:98
const int ast_audiosocket_init(const int svc, const char *id)
Send the initial message to an AudioSocket server.
struct ast_frame * ast_audiosocket_receive_frame(const int svc)
Receive an Asterisk frame from an AudioSocket server.
Main Channel structure associated with a channel.
static int unload_module(void)
const int ast_audiosocket_send_frame(const int svc, const struct ast_frame *f)
Send an Asterisk audio frame to an AudioSocket server.
Asterisk main include file. File version handling, generic pbx functions.
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
static int load_module(void)
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define LOG_WARNING
Definition: logger.h:274
#define AST_MALLOCD_DATA
#define ast_socket_nonblock(domain, type, protocol)
Create a non-blocking socket.
Definition: utils.h:1043
Universally unique identifier support.
static int handle_audiosocket_connection(const char *server, const struct ast_sockaddr addr, const int netsockfd)
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
#define NULL
Definition: resample.c:96
char * end
Definition: eagi_proxy.c:73
Socket address structure.
Definition: netsock2.h:97
#define ast_verb(level,...)
Definition: logger.h:463
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition: netsock2.h:521
#define ast_log
Definition: astobj2.c:42
General Asterisk PBX channel definitions.
#define ast_poll(a, b, c)
Definition: poll-compat.h:88
AudioSocket support functions.
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
#define LOG_ERROR
Definition: logger.h:285
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
int errno
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:260
#define ast_free(a)
Definition: astmm.h:182
#define MAX_CONNECT_TIMEOUT_MSEC
#define ast_frisolate(fr)
Makes a frame independent of any static storage.
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
struct ast_frame ast_null_frame
Definition: main/frame.c:79
const char * ast_channel_name(const struct ast_channel *chan)
Data structure associated with a single frame of data.
union ast_frame::@263 data
enum ast_frame_type frametype
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
Asterisk module definitions.
const int ast_audiosocket_connect(const char *server, struct ast_channel *chan)
Send the initial message to an AudioSocket server.
int ast_connect(int sockfd, const struct ast_sockaddr *addr)
Wrapper around connect(2) that uses struct ast_sockaddr.
Definition: netsock2.c:595
Media Format Cache API.
int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str, int flags, int family)
Parses a string with an IPv4 or IPv6 address and place results into an array.
Definition: netsock2.c:280