Asterisk - The Open Source Telephony Project GIT-master-2de1a68
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 <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"
37#include "asterisk/channel.h"
38#include "asterisk/module.h"
39#include "asterisk/uuid.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 */
57static 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",
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
99const 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
159end:
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
180const 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
209const 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
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
327static int load_module(void)
328{
329 ast_verb(5, "Loading AudioSocket Support module\n");
331}
332
333static int unload_module(void)
334{
335 ast_verb(5, "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,
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
char * end
Definition: eagi_proxy.c:73
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Generic File Format Support. Should be included by clients of the file handling routines....
Media Format Cache API.
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
@ PARSE_PORT_REQUIRE
#define ast_frisolate(fr)
Makes a frame independent of any static storage.
#define AST_MALLOCD_DATA
@ AST_FRAME_VOICE
struct ast_frame ast_null_frame
Definition: main/frame.c:79
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_WARNING
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:316
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:326
@ AST_MODULE_SUPPORT_EXTENDED
Definition: module.h:122
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_AF_UNSPEC
Definition: netsock2.h:54
int ast_connect(int sockfd, const struct ast_sockaddr *addr)
Wrapper around connect(2) that uses struct ast_sockaddr.
Definition: netsock2.c:595
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition: netsock2.h:517
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
#define ast_poll(a, b, c)
Definition: poll-compat.h:88
const int ast_audiosocket_init(const int svc, const char *id)
Send the initial message to an AudioSocket server.
const int ast_audiosocket_send_frame(const int svc, const struct ast_frame *f)
Send an Asterisk audio frame to an AudioSocket server.
struct ast_frame * ast_audiosocket_receive_frame(const int svc)
Receive an Asterisk frame from an AudioSocket server.
const int ast_audiosocket_connect(const char *server, struct ast_channel *chan)
Send the initial message to an AudioSocket server.
#define MAX_CONNECT_TIMEOUT_MSEC
static int load_module(void)
static int unload_module(void)
static int handle_audiosocket_connection(const char *server, const struct ast_sockaddr addr, const int netsockfd)
AudioSocket support functions.
#define NULL
Definition: resample.c:96
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Main Channel structure associated with a channel.
Data structure associated with a single frame of data.
union ast_frame::@226 data
enum ast_frame_type frametype
Socket address structure.
Definition: netsock2.h:97
struct sockaddr_storage ss
Definition: netsock2.h:98
#define ast_socket_nonblock(domain, type, protocol)
Create a non-blocking socket.
Definition: utils.h:1073
Universally unique identifier support.